srmech changelog¶
All notable changes to this package will be documented here. The format follows Keep a Changelog; this package uses semantic versioning.
[Unreleased]¶
Next development line: the deferred-from-v0.4.6 Tier-2 introspection ring buffer (mmap, for >1k events/sec). It stays deferred until an op proves the need — no consumer in the RBS-LM pipeline yet exceeds the Tier-1 flush-per-write rate. (The C-side srmech_progress_cb_t callback ABI — the OTHER deferred half — shipped in rc242 below.)
#693 determination (documentation only — NO version bump; no behavior change). Investigated whether the ThetaSum.is_zero interpolation degree bound can be tightened from Σe² to Σ|e|. Verdict: UNSOUND — NOT adopted; the conservative Σe² is retained at both bound sites (thetasum._struct_one_var base-case p-order band + thetasum._structural_is_zero._deg node count) and the C peer (srmech_thetasum_interp.c ti_deg). Rationale: the node count / p-band must bound the true elliptic degree of a theta-product in the interpolation variable = the quasi-period index = zeros-per-annulus, which is Σe² (a factor θ(c·vᵉ;p) gains multiplier v^{−e²} under v↦p·v, from Rosengren Eq. 1.6 / ellbase.Theta.canonicalize; confirmed by an independent explicit root count). Since e² > |e| for |e| ≥ 2, Σ|e| sits below the true degree and under-provisions the prover → a genuinely non-zero elliptic function can be falsely proved ≡ 0 (a false theorem). Explicit witness (single var, e=3): N(x) = 2·θ(2x³) −27·θ(3x³) +120·θ(4x³) −250·θ(5x³) +270·θ(6x³) −147·θ(7x³) +32·θ(8x³) (all ;p) is exactly non-zero (lowest q-expansion coeff (p⁶,x⁻⁹)=−1/112; stable 0.180756 at p=½,x=¾), yet Σ|e| (band k=5) misses the p⁶ term while Σe² (band k=11) correctly returns False. (Distinct from #692, which sized the arena/ws_bound memory band — this is the soundness degree band.) Full analysis: docs/srmech/notes/thetasum_is_zero_degree_bound_693.md; standing regression guard: tests/test_thetasum_degree_bound_soundness_693.py.
[0.9.0rc306]¶
srmech_genome_section_counts becomes CALLER-ARENA (§102 / task #899). The op that re-derives {global_id: n_sections} from a plasmid store scanned against THREE file-scope static scratch buffers — a 32 MiB catalog arena, a 2^18-slot count table, a 64 KiB window (+ a static id counter). That made it BOTH corpus-capped (srmech_genome_arena_bytes's ~2.7 KiB/chromosome term filled the 32 MiB arena at ~11,000 sections, past which a native scan silently declined to the pure body) AND non-reentrant — an exception to the reentrant-C-core claim (#772) and a real hazard for a threaded host embedding libsrmech.
ABI 8 → 9 — the first ORDINARY-kind bump (an existing exported signature changed, not a callback typedef, not a removal). srmech_genome_section_counts gains (void *ws, size_t ws_len); the count table + region window are carved off the caller ws and the untouched TAIL is the catalog arena genome_obtain_manifest parses into — the exact caller-arena route the JSON/TOML parsers (rc159/rc160) and the CD dimension (#933) already use. No static scan state remains: the op is reentrant on disjoint ws buffers, and the corpus bound is whatever ws the caller sizes. No new op — describe()["tools"]["total"] stays 478; carriers stay 25; CD_MAX_DIM 256; CEIL_WIRE_GLUE_GAPS 10; GENOME_FORMAT_VERSION stays 15 (no on-disk change — the counts stay byte-identical to the pure body).
- The sizing helper. New C symbol
srmech_genome_section_counts_arena_bytes(body_len, n_chroms, out_cap)(mirroring the other*_arena_byteshelpers): catalog term =srmech_genome_arena_bytes(body_len, n_chroms, 0)(scales with the corpus), count table sized to holdout_capdistinct ids under the ¾ open-addressing load bound (floored so a tiny store still gets headroom), + a 64 KiB window + alignment slop. It is a pure-integer helper, NOT a registered ToolEntry (sotools.totalis unchanged). - BOTH projections change in lockstep. The Python
_native.pyctypes binding gains thews/ws_lenparams;EXPECTED_ABI_VERSION8 → 9;genome_section_counts_cnow sizeswsvia the helper (frombody_len= turns.bin bytes andn_chroms= the manifest chromosome countplasmid.section_countsalready has) and growsws+ the out arrays together on overflow (exact retry when the table held the ids and only the out arrays were short; geometric otherwise), so a corpus with any number of distinct ids — past the old 196,608-id ceiling — censuses natively given memory. The pure Python body is UNTOUCHED, so C-vs-Python parity holds (native == pure, byte-for-byte). - The count table's 2^18 ceiling is gone too. Because the table is sized from
out_cap(which the retry loop grows) rather than a compile-time constant. - The
c_claimsmanifest updates (regenerated):srmech.amsc.plasmid.section_countsnow claims BOTHsrmech_genome_section_countsandsrmech_genome_section_counts_arena_bytes. The tool/carrier/class/responsion registries andtool_schema_sha256are UNCHANGED (no ToolEntry moved). - Tests. New
test_plasmid_section_counts_arena_rc306.pyproves: the sizing helper scales past the old 32 MiB static (so past the ~11k cap) and past the 196,608-id ceiling; the three statics + two cap macros are gone from the C source (the reentrancy proof); native == pure across small AND larger fixtures; and the caller-arena retry-grow engages (and stays correct) when the initial capacity is forced tiny. The rc280 C smoke (test_srmech_genome.c, 196 asserts) and the rc280 pytest (18) pass unchanged against the new signature. All 16 ABI pins updated 8 → 9.
[0.9.0rc305]¶
Rebased onto rc304 (which added genome_from_graph's attestation= parameter); this rc extends the SAME op's ToolEntry additively with composes/preserves, and the generated tool registry is regenerated against the rc304 genome_from_graph so the byte-identity ratchet holds.
Two deliverables in one rc: (A) ToolEntry.composes / .preserves — the Siona compose-a-cascade CAPSTONE (#943); (B) the test_bus_aio discovery-cleanup flake FIXED AT ROOT (#920), not worked around. No new op — describe()["tools"]["total"] stays 478; no new C symbol — ABI stays 8; carriers stay 25, CD_MAX_DIM 256, CEIL_WIRE_GLUE_GAPS 10. The c_claims manifest is unchanged. The tool_schema_sha256 DOES move (two new keys ride every composite ToolEntry's canonical JSON) — the C serialiser mirrors in lockstep so the byte-identity + hash ratchets recompute clean.
#943 — composes / preserves: a cascade is CROSS-OP, and now it is DATA¶
- The gap.
describe()/exampleare PER-OP; the knowledge to CHAIN ops — "op A feeds op B, preserving invariant X" — lived only in CHANGELOG prose Siona cannot read as data. rc305 adds two STRUCTURED fields to everyToolEntry:composes(the ordered sub-ops an op is built from; empty for a leaf — the correct default) andpreserves(the invariants it maintains). - Claims that are TRUE — the exact defect class this rc line corrects. A populated
composescan never name a phantom op:test_composes_preserves_rc305.pysweeps the WHOLE registry and fails if anycomposesentry is not a real registered tool or anypreservesentry is blank. The F1299 worked example,genome_from_graph, is pinned to its TRACED cascade[genome_partition → graph_to_kernel → mint_strand → genome_save → genome_census](read offgenome.py, call-order), with the byte-exactkernel_to_graphround-trip stated inpreserves. - Coverage, stated honestly. Only
genome_from_graphcarries composition data this rc — it is the one cascade traced end-to-end against its implementation. Every other op keeps the empty-tuple leaf default (NOT fabricated composition). The field is the vehicle; population grows op-by-op as each cascade is traced, under the whole-registry claim-is-true gate. - The data rides the curation floor (
_tool_docs_curated.py→gen_tool_docs.py→_tool_docs.py→_apply_docs), the SAME pathexplanation/examplealready use, so a hand-written registration literal still wins and the un-rederivable-prose guard stays satisfied. - THE C-SERIALISER MIRROR (the ripple most agents miss). The tool registry is rebuilt field-by-field in C (
srmech_tool_schema.c) and the C-vs-Python byte-identity +tool_schema_sha256hash-ratchet compare against it. Adding two emitted keys means the C side MUST emit them too:srmech_tool_entry_tgrewcomposes/preservesstring-array fields (APPEND-only struct growth → ABI stays 8; the ctypes mirror reads only the field prefix, unchanged),gen_tool_registry.pybakes the per-entry arrays, andts_emit_entryemits"composes"(sorted betweencategoryandexample) +"preserves"(betweenparametersandreturns). Verified BYTE-IDENTICAL: C JSON == Python SSoT (679459 bytes),sha256(C) == tool_schema_sha256.
#920 — the test_bus_aio flake, fixed at its ROOT (the readiness-race diagnosis was refuted by the code)¶
- REPRODUCED first, and the first diagnosis did NOT hold. The suspected cause —
_wait_for_endpointtrusting analiveflag that flips true before the server accepts — is refuted by the code:by_name().alivealready verifies via a real connect-probe (_endpoint_alive_uds), so the helper already waits for genuine acceptance. 640+ hammered iterations (unloaded, under 8 CPU burners, and the full bus family under--dist load) never reproduced a send-timeout. The ACTUAL root cause is the one the rc283 conftest workaround's own CHANGELOG note recorded:list_endpoints(cleanup_dead=True)DELETES any socket whose 50 ms connect-probe fails — but a server inside itsbind()→listen()window, or one whose accept backlog is momentarily full, refuses/times-out that probe IDENTICALLY to a crashed server's stale file. Under a shared~/.srmech/(xdist workers share HOME) one worker's routine discovery sweep therefore unlinks another worker's IN-FLIGHT socket, surfacing asFileNotFoundError: no bus endpointorBusError: reader exited; peer closed. Reproduced DETERMINISTICALLY: a bound-not-listening socket AND a backlog-full listening socket are both judged dead and unlinked by a sweep. - Root fix — cleanup now CONFIRMS death before unlinking. A pathname-bound UDS socket appears in
/proc/net/unixthe instantbind()returns, independent oflisten()/accept()— the liveness signal the connect-probe structurally cannot provide.list_endpointstakes one/proc/net/unixsnapshot per sweep and unlinks a dead-LOOKING UDS registration ONLY when it is confirmed absent there (truly orphaned). Where the proof is unavailable (macOS/BSD — no/proc/net/unix) cleanup DECLINES to delete, since a mis-deletion corrupts a live peer while a genuinely-stale file self-heals at the nextbind().alivereporting is unchanged; only the destructive decision is narrowed. Genuine cleanup is preserved — an orphaned file with no live owner is still removed (verified). The rc283 conftest HOME-isolation STAYS as defence-in-depth (it also carries the macOSsun_pathcap fix); this makes the LIBRARY correct so the workaround is no longer load-bearing. - Proof the flake is dead:
test_bus_discovery_cleanup_rc305.py— a bound-not-listening socket and a backlog-full socket each SURVIVE a sweep, a real accepting endpoint survives 25 concurrent sweeps, and an orphaned file is still cleaned; each survive-assertion was made to fail once (fix defeated) before trusting it. The wholetest_bus_aio.pyfamily runs clean under-n auto.
[0.9.0rc304]¶
A corpus genome can now attest its OWN source. genome_from_graph / genome_save take a caller attestation= whose fields OVERRIDE the srmech-default MPR written into manifest.json, so an attested corpus genome (e.g. a simplewiki dump whose true source is https://dumps.wikimedia.org/simplewiki/latest/ under CC-BY-SA-4.0) records its REAL provenance instead of misattributing itself to srmech.net/genome/persistence / 1970-01-01T00:00:00Z. This is an MPM integrity fix, not cosmetics: the genome directory is the SSoT with NO sidecar files (§41/F1300), so the manifest is the ONLY legitimate home for a genome's source attestation — before rc304 no-sidecar and MPR-provenance were in direct conflict, and a real corpus genome asserted a false source, exactly the citation-drift the AMSC attestation block exists to prevent. No new op, no new C symbol; ABI stays 8. describe()["tools"]["total"] stays 478 (only genome_from_graph's ToolEntry gains a parameter); carriers stay 25, CD_MAX_DIM stays 256, CEIL_WIRE_GLUE_GAPS stays 10, and the c_claims manifest is unchanged.
§113 / #1466 (task #942) — a caller-supplied genome source attestation¶
genome_from_graph(…, attestation=)andgenome_save(…, attestation=)— a caller MPR source-attestation whose provided fields override the srmech default inmanifest.json. Threadedgenome_from_graph → genome_save → _manifest_record;graph_to_kernelonly builds the strand (it writes no manifest) and is untouched.- OVERRIDE-ONLY, restricted to the five SOURCE-identity fields (
source_doi/source_url/license/retrieved_at/response_sha256). A provided field replaces its default; an absent field keeps its default (a partial dict never blanks the rest — a caller supplyingsource_url+licensekeeps srmech's other fields). The four ENCODER-identity fields (parser_version/parser_rule_hash/collector_descriptor_path/collector_descriptor_hash) stay srmech-owned, so a caller attests the corpus source WITHOUT being able to misreport which srmech version / rule wrote the bytes. Overridingresponse_sha256(an MPR's response = the corpus dump) is SAFE: genome body integrity is anchored on the separatedata["body_sha256"](re-hashed ongenome_load), NOT onattestation.response_sha256, so a dump-sha override cannot weaken integrity (proven). - A bad override is REJECTED before any bytes hit disk — a non-dict (
TypeError), an unknown / disallowed key (ValueError, incl. a typo'dsource_urior an attempt to overwrite an encoder field — the very misattribution the parameter prevents), or a value that makes the merged block an invalid MPR (MPRValidationErrorviasrmech.amsc.format.validate_mpr_record). Never written silently. - Multi-implementation parity (ADR-0009 / the capability is the invariant). The native
srmech_genome_savewrites the srmech DEFAULT MPR and takes no override, so the override is applied by the PURE manifest write (the already-materialisedbody_bytes+ the overridden head-only manifest). PROVEN byte-identical across projections: the native-default and pure-default manifests are byte-for-byte equal, and an override manifest is byte-for-byte equal whether native is present or not;turns.binis byte-identical with or without an attestation (the override touches ONLY the manifest). The default save is byte-identical to rc303 (modulo the version string). C conclusion: the bare-Csrmech_genome_saveentry still writes the default — minting a custom source attestation from a pure-C host (an additivesrmech_genome_save_attestedsymbol; ABI-neutral) is a tracked follow-up, deliberately deferred to keep this rc'sc_claims/ ABI / registry surface unchanged. - Proof:
tests/test_genome_attestation_rc304.py(19 tests — round-trip, disk-persist, MPR-validate, census read-back, response_sha256-integrity, override-only partial, native≡pure parity, five rejection cases). Ripple:_tool_docs.pysettled FIRST (example re-derives with the new param;--accept-seed-driftfor the signature change, the curated explanation survives), thensrmech_tool_registry.cregenerated (param count 10→11); the other three C registries byte-identical; generated in WSL, CRLF-restored;.sorebuilt before any test.
[0.9.0rc303]¶
The df-gated ABOUTNESS grounding encoder ships as the first RBS-LM op in the LLM-facing tool_schema — "which srmech op does this utterance want?" is now one call (srmech.rbs_lm.encode_aboutness), grounding at 72% top-1 over the tool_schema where the shipped encode_sentence_l3 floors at 17%. The F1008 (SIONA-INFER-2, research/rbs-lm-rolling-2 PR #687) recipe promoted from research to a packaged capability, on the STRUCTURE-BEARING side of the F1260 axis. No new C symbol; ABI stays 8. describe()["tools"]["total"] 477 → 478 (one op); carriers stay 25, CD_MAX_DIM stays 256, CEIL_WIRE_GLUE_GAPS stays 10, the non_compute split is untouched, and the c_claims manifest is unchanged.
§112 / #1462 (task #941) — the df-gated grounding encoder¶
- Premise VERIFIED by measurement, not trusted. The tracker claimed
encode_sentence_l3grounds at the ~⅕ orthogonality floor "because it rides seed-basedencode_word_k4". Measured over the shippedtool_schemaon the 18-utterance F1008 eval: the shipped encoder grounds at top-1 3/18 = 17% in BOTH modes —byteglyph(the default, NOT the seed path) andwordhash. So the floor is real but the named mechanism is imprecise: the cause is the MISSING doc-frequency gate / name-weighting / order-aware bigrams, not the token backend. Generating code + NDJSON:docs/srmech/notes/rc303_df_grounding_measurement.py(+.ndjson). srmech.rbs_lm.encode_aboutness(text, *, D, df=, n_docs=, name=, token_mode=…)— the F1008 recipe as one op: (1) a doc-frequency aboutness GATE (F768/F984 — drop catalog-wide function words), (2) NAME-weighting (F769 — an op's own name tokens count 3× unigram + 2× bigram), (3) order-aware BIGRAMS ([[feedback_never_bag_of_words]]—(klein,4)≠(klein,gordon)), plus letter-digit tokenization (klein4→klein 4). Measured HERE: top-1 13/18 = 72.2%, top-3 77.8% over the tool_schema — the number from THIS repo's committed generating code, not a quoted figure. It is lower than the research branch's F1008 78% (over 347 tools) for a mundane, non-regression reason: this eval is n=18 and one of its targets,klein4_random, was removed from the schema in rc292, so it is an automatic miss for every encoder (13/17 = 76% on the resolvable targets). Report is the reproduced 72.2%, honestly, over the committed NDJSON. Companion one-call surfaceground_tool_schema(utterance)fits the gate over the live catalog and returns the top-k tools.- Structure-bearing, NOT high-diffusion (F1260). Grounding is a REPRESENTATION problem, so tokens are minted via the COMPOSED
klein4_encode_bytes(defaulttoken_mode="byteglyph"), NOT the high-diffusion word-hashklein4_address— a SHA avalanche makes a good ADDRESS but destroys morphology (F1260). The discriminant holds:cat/cats= 0.66 whilecat/dog= 0.25 (byteglyph); the offeredtoken_mode="address"dual floors BOTH (0.26 / 0.25) and is documented as NOT structure-bearing. The tradeoff is honest and small: byteglyph MATCHES the address recipe on top-1 (both 72%) at a modest top-3 cost (78% vs the address dual's 89%) — the morphological similarity byteglyph adds is exactly what passes F1260 and it costs nothing at top-1, where grounding actually decides. - Classification honesty (
composition_of_c, no C symbol). The representation-bearing compute is a transitive composition ofc_dispatchedKlein-4 primitives (klein4_encode_bytes→bind/bundle/random); the tokenizer / gate / name-weighting are Python SELECTION glue over which C-backed atoms compose — exactly the existing rbs_lm ladder. There is NO dedicatedsrmech_rbs_lm_encode_aboutnesssymbol, so this is NOTc_dispatched— the honest narrow bucket iscomposition_of_c(bothencode_aboutnessandground_tool_schema; the tokenizer + df-counter stay PRIVATE so the ledger never has to account for a bare tokenizer as a compute op). Rosetta: twocomposition_of_crows; all four debt ceilings stay 0. - Ripple gate applied in order.
_tool_docs.pysettled FIRST (+1 line, executed example, no refusal), then all four generated C registries:toolmoved (+the op) andcarrierrenumbered (the auto-derived carrier back-index picks up the op's HV references — a legitimate transitive ripple, confirmed idempotent),class/responsionbyte-identical; generated in WSL and CRLF-restored..sorebuilt before any test.describe()["tools"]["total"]477 → 478 across 56 assertions in 50 modules in seven literal forms.
[0.9.0rc302]¶
The Class-I modular family is now DSL-DECLARABLE and DISCOVERABLE — a modular-arithmetic cascade (an LCG, a hash, the PCG64 step) can be composed as a TOML chain, not only hand-written in Python. Before rc302 only cyclic_gcd lived in the cascade catalog, so a modular cascade could not be declared via srmech.dsl.chain / make_class at all. No new C symbol; ABI stays 8. describe()["tools"]["total"] 470 → 477; carriers stay 25, CD_MAX_DIM stays 256, CEIL_WIRE_GLUE_GAPS stays 10, the non_compute split is untouched. The c_claims manifest gains exactly one entry (cyclic.bigint_mul → srmech_bigint_mul); all other new ops are composition_of_c.
§110 / #1460 — chain-register the Class-I modular family + a 128-bit-capable modular multiply¶
- The modular family becomes cascade ops (5 new catalog TOMLs).
cyclic_mod_mul/cyclic_mod_add/cyclic_mod_pow/cyclic_mod_invjoin the cascade catalog as thin delegations to the already-c_dispatchedsrmech.amsc.cyclic.*primitives (composition_of_c; no native dispatch of their own). Following the DSL chain contract the piped value is the first positional arg and the operands are bound stage kwargs, so an LCG step readschain().then("cyclic_mod_mul", b=MULT, n=MOD).then("cyclic_mod_add", b=INC, n=MOD)— proven to match hand-composed Python over the catalog list-op count 15 → 20. cyclic.mod_mul_wide(a, b, n)— the uncapped modular multiply.mod_mulstays correctly capped at uint64 (it binds the fixed-64-bit ABIsrmech_mod_mul);mod_mul_wideis the separate uncapped companion (chosen over abits=param so the capped guard is never weakened). It routes the product through srmech's own C bignumsrmech_bigint_mul(via the newcyclic.bigint_mul) and reduces modn, so the raw PCG64 128-bit LCG step (n = 2**128, multiplier0x2360ed051fc65da44385df649fccf645) is expressible where the capped op cannot.composition_of_c— packaging an existing capability, not a new one. Also a cascade op (cyclic_mod_mul_wide).
§112 / #1462 — register the bignum multiply + give summaries user-facing aboutness¶
cyclic.bigint_mul+cyclic.mod_mul_widejoin the publictool_schema. The bignum multiply (previously only_native.bigint_mul_c) is now a discoverable, always-correct public op so Siona can recommend the op a wide modular cascade relies on.cyclic.bigint_mulisc_dispatched(srmech_bigint_mul); the 5 cascade ops +mod_mul_wideare registered too (7 new ToolEntries total).- Aboutness clauses added (implementation words kept).
cyclic.mod_mulnow leads with "Modular multiply — (a * b) mod n" (keeping "russian-peasant doubling");cyclic.mod_powleads with "Modular exponentiation" (keeping "square-and-multiply");cascade.magnitudenow says "Absolute value |x|" (keeping "Class K pin-slot at zero"). A caller searching "modular multiply" / "absolute value" now hits the tool.
[0.9.0rc301]¶
CDRegister is srmech's general-purpose ADDRESSING layer — a content-agnostic floor, not a surface that subsumes every class. rc297 brought the general N-slot Cayley–Dickson register in-tree as an addressing object; rc301 ports the four VALUE-operations that until now lived only on SedenionRegister — couple_working / uncouple_working (the Class-M reversible working word) and carry / correct (the Hamming EC block) — onto CDRegister as its two OPTIONAL layers, and establishes the three-layer decomposition (CORE addressing / OPT coupling / OPT error correction) as the register's architecture of record. No new C symbol; ABI stays 8. describe()["tools"]["total"] 466 → 470 (four content-agnostic ops become first-class MCP tools); carriers stay 25, CD_MAX_DIM stays 256, CEIL_WIRE_GLUE_GAPS stays 10, the non_compute split is untouched, and the c_claims manifest is unchanged. The committed contract is docs/srmech/notes/cd_register_addressing_layer_contract_rc301.md.
#938 — port the reversible / error-correction operations onto the addressing floor¶
-
The working-set cap is DERIVED, not chosen:
min(dim, 8) − 1. The reversible coupling layer binds≤ len(working_block()) − 1values, read from the dim-scaled accessor — never a hardcoded 7. Hurwitz (1898): normed division algebras exist only at dim ½/4/8 (imaginary units 0/⅓/7). Below 8 the cap is the algebra's own imaginary count (dim 2 → 1, dim 4 → 3); at/above 8 it PINS at 7 becausee0..e7is an octonion subalgebra of every higher rung — a reversible word survives there but cannot grow. dim 1 (ℝ) is the degenerate empty-coupling base: it couples nothing and uncouples to nothing, a legal instantiation that behaves rather than crashes. The addressing cap (dim→ 256) and the coupling cap (min(dim,8)) are DISJOINT boundaries and stay so — addressing rides on the basis product being a signed permutation, which zero divisors (built from sums of basis elements) never touch. -
The two OPT layers are off by default — a bare register is a pure signed-pointer. Two independent constructor flags,
coupling=Falseanderror_correction=False: a pure-addressing consumer (an L-style spectrum holder, address-complete to 256) does not pay for the coupling/EC machinery. When off, the four value-operations RAISE a directed error (gated, not merely unused);navigatepropagates the flags to the routed register. The flags gate method availability only — the ops are stateless functions of their arguments, so a bare register carries nothing extra. The EC axis is INDEPENDENT ofdim: the Hamming block size is set byn(parameterised, default 3 = Hamming(7,4), the octonion's own Fano plane), not by the slot count. -
No new C symbol — the ops are compositions of already-C-backed primitives.
couple_workingcomposeshypercomplex_couple(whose octonion multiply dispatches to the standalone-Csrmech_hypercomplex_couple_q61);carry/correctcomposehamming_encode/hamming_decode_correct(thesrmech_hamming_*peers). A dedicatedsrmech_cd_couple_workingwould besrmech_hypercomplex_couple_q61plus a trivial cap guard — a laundered duplicate the no-duplication discipline forbids. The honest Rosetta bucket iscomposition_of_c, exactly as thesed_*adapters are classified; each op therefore exists fully in C as a composition of reachable header symbols. -
Oracle parity, bit-exact.
CDRegister(dim=16, coupling=True, error_correction=True)reproduces the shippedSedenionRegister's four methods byte-for-byte (identical floats, not a tolerance) — because both delegate to the same primitives and at dim 16 the cap coincides at 7.SedenionRegisterdeliberately REMAINS an independent class (not a dim-16CDRegisteralias) — the faithfulness gate depends on it, sotest_cd_register_ops_rc301.pyasserts the oracle stays independent. -
The graded universal-addressing proof (M / N / L), measured not assumed. M:
couple_working/uncouple_workingIS a Class-M reversible bind — the SAME role as the shippedhdc.bindfamily but a DISTINCT operation on a different carrier (hdc.bindis self-inverse XOR over 𝔽₂ᴰ;couple_workingneeds the conjugate-twiddle inverse over 𝕆). N: aCDRegisterover the exact-rationalQcarrier is a coherent addressable rational store (Q rides the content-agnostic CORE layer as content). L (the falsifier): a bare register STORES a Laplacian spectrum while the eigendecomposition stays inMat(jacobi_eigvals) — L fits as STORAGE, not OPERATION, confirming addressing is a floor and not a subsumer. Generating code + NDJSON results committed atdocs/srmech/notes/cd_register_addressing_proof_rc301.ndjson.
[0.9.0rc300]¶
A C-backed claim must be checkable against the loaded library — not merely asserted in a fixture. rc299 reclassified mat_eigvals to c_dispatched ("routes to a srmech_* C symbol"). That is a claim about the loaded library, and its dispatch site is gated on hasattr(_native.LIB, "srmech_mat_eigvals_ws") → return None → pure sweep. If the symbol is absent the answer stays correct — the fallback is a complete alternative — but the c_dispatched claim is silently FALSE and, before this rc, nothing anywhere said so. No new C symbols; ABI stays 8; describe()["tools"]["total"] stays 466, carriers stay 25, CEIL_WIRE_GLUE_GAPS stays 10, the non_compute _FULL_SPLIT is untouched. describe() gains one key, c_claims.
#938 — a silently-missing C symbol stops being invisible¶
-
Ground truth first: the
has_native_*helpers are not the fix. All 188has_native_*()predicates in_native.pyare purehasattr(LIB, "srmech_x")wrappers (verified by parsing every body — zero exceptions). So migrating the ~67 rawhasattrdispatch gates to helpers would change style and fix nothing; the defect is not how the gate is spelled but that nothing cross-checks the claim the gate implies. Left the gates as they are. -
Why ABI matching does not already cover this.
_nativesetsHAS_NATIVE=Falseon an ABI mismatch, so a loaded library always matches ABI 8 — but the header adds symbols ABI-additively ("new symbols only, soSRMECH_ABI_VERSIONstays N" recurs ~100× insrmech.h). ABI pins the WIRE FORMAT of existing exports, not the symbol SET. A stale-but-ABI-8 build is therefore a reachable state whose pure fallbacks are correct and whosec_dispatchedclaims are silently wrong — exactly the "stale.soruns the pure path" failure this project has lost time to before. -
The fix: a committed op→symbol claim manifest, checked against the loaded library.
tools/gen_c_claims.pywalks eachc_dispatchedop's OWN dispatch path (private helpers in-module,*_cshims, and thehas_native_*bridge used by the lazygetattr(nat, "has_native_x")idiom), collects everysrmech_*token, and keeps only those DECLARED inc/include/srmech.h— a filter that dropped exactly one incidental look-alike (asrmech_kext_tempfile prefix) and would surface a typo'd dispatch symbol that could never resolve. Result committed assrmech/amsc/_c_claims.py(ships in BOTH wheels; the Rosetta ledger is a test fixture and does not)._native.c_claim_report()resolves each claimed symbol against the loadedLIB; surfaced asdescribe()["c_claims"]. 240 of the 263c_dispatchedops get an attributable symbol (327 distinct symbols); a genuine stale build — rebuilt withsrmech_mat_eigvals_wsrenamed away — flipsconsistentto False and NAMES the op and its missing symbols, and turns the suite red. That state was completely silent before rc300. -
Report, not raise — because the pure wheel legitimately has no library.
HAS_NATIVE=Falseis the pure wheel's normal state; a stale native build still computes correct answers. Raising at import would break a legitimate install (and Pyodide) over a build defect that is not a correctness defect. Soc_claim_report()reports{native, checked_ops, checked_symbols, unresolved, unverifiable, consistent}and never raises; the LOUD failure lives intests/test_c_claim_resolution_rc300.py, which fails on any unresolved claim. On a pure installnativeis False,consistentTrue, nothing contradicted. -
The blind spot is named and ratcheted, not hidden. The static walk cannot attribute a symbol to 23 of the 263 ops (dispatch reached through a class method or registry indirection). Rather than let that gap pass as coverage — the
#918/#922/#937failure mode of a check that passes because it isn't looking — they are enumerated inUNVERIFIABLE_CLAIMSunder a DOWN-ONLYUNVERIFIABLE_CEILING, and a partition test assertschecked ∪ unverifiable == every c_dispatched opso none can silently vanish from both. The check DETECTING a missing symbol is itself asserted (against a library proxy with a symbol hidden), so this test cannot be a fifth instance of the same defect. -
Two genuinely-silent TEST guards fixed while here (distinct from dispatch gates — a dispatch fallback returns correct answers, a test guard loses coverage invisibly):
test_holo_bundle_accumulate_rc155.pyhad a barereturnwhere its docstring promised a skip, silently evaporating the entire C-vs-pure byte-identity proof;test_svd_qr_f64_rc140.pysilently dropped the only independentS²vseigenvalues(AᵀA)cross-check. Both nowpytest.skipwith a named reason. A third (test_c_standalone_honor_rc156.py, a symbol-guardedifwith noelse) is left as-is deliberately: its pure/public-parity block is real coverage a skip would discard, and its symbol (srmech_exact_dft_i64) is now checked systemically by the claim manifest. -
ADR-0009 §6(b) status note added. The manifest is a bounded first step on the "C-host capability manifest" that ADR-0009 authorizes but does not implement: it makes false the ADR's observations that "the only test that reads
srmech.hreads it for the version string" and (partially) that "nothing would notice if C lost a capability." It does NOT close (b) — the direction is still Python-rooted (is the symbol this op claims present?), it says nothing about whether a reached symbol suffices over the real input domain, and 23 ops stay unverifiable.
[0.9.0rc299]¶
Two false descriptions, one shared root: a capability claim that outran the code, and a precision claim that outran the arithmetic. #918 builds the general non-Hermitian eigensolver C never had, so mat_eigvals's composition_of_c classification becomes TRUE instead of being narrowed; #919 makes the exact-rational √ cascade relative-precision, so hypot stops returning an exact 0.0 — and, less visibly, stops returning wrong non-zeros above that floor. New C symbols srmech_mat_eigvals_ws / srmech_mat_eigvals_ws_size (additive → ABI stays 8), describe()["tools"]["total"] stays 466, carriers stay 25, CEIL_WIRE_GLUE_GAPS stays 10.
#918 — mat_eigvals claimed a C parity it did not have¶
-
Ground truth first, and it was worse than the issue stated. The C tree had exactly three eigen-paths and none was general:
srmech_jacobi_eigvals(real symmetric),srmech_hermitian_eigendecompose_ws(complex Hermitian), and the exact-integersrmech_eigvec_exact/srmech_complex_isolate/srmech_jordan_chains(char-poly + Sturm / argument-principle, integer matrices only). No Hessenberg reduction existed anywhere in C — verified by grep acrosssrc/andinclude/, not inferred. Meanwhilemat_eigvals's balancing, Hessenberg reduction, deflation loop, Wilkinson shift ladder and{QR}were Python-only; the ONLY step reaching C was theRQrecombine viamat_matmul. -
The classification was wrong for ALL inputs, not just non-Hermitian ones — which the issue did not say.
mat_eigvalshas no Hermitian fast path: it never consultssrmech_hermitian_eigendecompose_ws, even for Hermitian input. It goes balance → Hessenberg → shifted-QR unconditionally. So "a bare-C host cannot eigensolve non-Hermitian matrices" understated it — a bare-C host could not runmat_eigvalsat all. -
Why the ratchet that exists for exactly this did not fire.
test_rosetta_transitive_standalone.pyhas two layers. The transitive walk treats everycomposition_of_cop as a LEAF and stops, so it sawmat_matmul(c_dispatched) and passed. The stronger WIRE-GLUE ratchet — the one that asks whether the op's own GLUE is C-reachable — is scoped to_WIRE_FORMAT_MODULES = ("srmech.amsc.genome", "srmech.amsc.plasmid")plusrecursive_cut, and its header names pure-math ops as deliberately out of scope because "they compose C primitives over carriers with no format to get wrong". That reasoning holds for a dot product. It does not hold here: format_eigvalsthe glue is the mathematics, and C supplied a matrix multiply. Calling that "a composition of C" is like calling an eigensolver a composition of multiplication.CEIL_WIRE_GLUE_GAPSdid not move becausemat_eigvalswas never inside the ceiling's scope — recorded here because the ceiling reading "10, unchanged" would otherwise imply this gap was counted, and it was not. -
Branch taken: (a) — build the C peer.
c/src/srmech_eigvals.c, ~470 lines, mirrors the Python body operation-for-operation: Parlett–Reinsch radix-2 balancing (exact power-of-two similarity), Householder reduction to upper-Hessenberg, negligible-subdiagonal pinning to structural zero, active-block search, Wilkinson shift with the EISPACK exceptional-shift cadence atit==10/it==20, per-step Householder{QR}, and closed-formn=1/ trailing-2×2deflation. JPL-clean with no new Rule-5 exemptions (the two violations the ratchet caught — a 74-line driver and a 0-assertws_size— were fixed by splitting the sweep out and adding real overflow assertions, not by adding to the exempt list). No libm (roots go through the sharedsrmech_rational_sqrt), no<complex.h>(interleaved(re, im)), no malloc (caller arena), no goto, no recursion, noabs()— every sign decision is a Class-K pin-slot with Class-C re-application. -
PARITY: NUMERIC (FPU-tol), and measured rather than asserted. Both projections run the same operation sequence in IEEE double and share
srmech_rational_sqrtbit-for-bit. The one honest divergence is the complex MODULUS: Python roots an EXACT rational sum-of-squares (arbitrary-precision Class-N), C uses the scaled float formm·√(1+r²). Measured: on real symmetric graph Laplacians the two projections are bit-identical (30 relabelled cases across 6 graph families — the modulus of a real number is exact on both sides, so the tolerance is not even consumed); on 140 random general complex / real-non-symmetric matrices spanning sizes 2–10 and scales 1e-6…1e6, the worst RELATIVE multiset deviation is 2.4e-14. Closed forms are exact or near: rotation →±iand Pauli-Y →±1to 0.0, defective Jordan[[2,1],[0,2]]→2,2to 0.0, companion ofxⁿ−1→ the roots of unity to ~8e-16. -
Classification moves
composition_of_c→c_dispatchedon a claim that is now true.matrix_cascades.eigvals, which delegates to it, stayscomposition_of_c— and that row is now honest too, for the first time. No debt ceiling moved (this is a bucket improvement, not a debt paydown);CEIL_PYTHON_ONLY_DEBT,CEIL_C_EXISTS_UNBOUND,CEIL_BIGNUM_REFERENCEandCEIL_NUMPY_CARRIERare untouched. -
The
ToolEntrysummary was rewritten, because it described the op as a Python sweep with "the RQ recombine routed through the native mat_matmul" — accurate before this rc and misleading after it. That is the onlytools.total-surface edit and it moved no count (466 → 466); it rippled intosrmech_tool_registry.c(+402 bytes) and into no other registry, which is the expected shape for a summary-only change. -
tests/test_mat_eigvals_c_peer_rc299.py(32 tests) pins the capability claim itself (the symbol must exist AND be reachable — a symbol that exists but is never called is a laundered gap), the matrix classes no pre-rc299 C path could touch, C-vs-Python parity in both the bit-exact and FPU-tol regimes, workspace-undersize refusal, and — carried over from rc285 —λ_min == 0and vertex-relabelling invariance asserted on the NATIVE path, so the compiled projection cannot quietly acquire the defect the Python one shed.
#919 — _fhypot returned exactly 0.0, and wrong numbers well above that¶
-
The defect is broader than
_fhypotand broader than zero. The root isrational._sqrt_rational, which floors√(num/den)onto a FIXED2^-kgrid — absolute precision. At the shipped defaultk = 54that is a quantum of2^-54 ≈ 5.55e-17, and both public consumers of the exact-rational root inherited it:hypot(always) andsqrt(on aQinput — a path#919does not mention and that is equally broken:sqrt(Q(1, 10^40))returned0.0). Below2^-54the root floors to exactly0.0. Above it the value is a plausible-looking non-zero that is simply wrong: 44% low at 1e-16, 2.4e-4 low at 1e-13, 5.3e-10 low at 1e-8. That band is the more dangerous half and is precisely why a!= 0.0guard would NOT have been a sufficient fix. -
The stated premise was false, and that changed the fix.
#919framed the floor as a real limit — "a bounded-denominator rational cascade genuinely cannot represent arbitrarily small magnitudes". It can.sqrt's FLOAT path already proved it: it decomposesx = M·2^eand carries an EXACT power-of-two scale, so it is relative-precision at every magnitude (sqrt(1e-300)is exact).Qis an arbitrary-precision integer pair. The floor was the hard-codedk, never the carrier. So the fix is not a guarded API, not a documented floor, and not an epsilon:_sqrt_relative_ksizes the grid to the radicand, which is what the float path always did. -
Backward compatibility is exact where it matters. For a radicand
≥ 1the selector returnskUNCHANGED, so every such value is byte-identical to pre-rc299 —hypot(3,4) == Q(5,1), and the_tool_docsexamplehypot(1.0,1.0) == Q(12738103345051545, 9007199254740992)— which is what keeps this repair entirely off the doc / registry ripple surface. An explicitprecision_bits=keeps its documented literal ABSOLUTE meaning, so the π cascade is untouched. -
Result: ~1 ulp at every magnitude. Differential-tested against libm over 220 orders of magnitude with both components live: worst relative error 2.2e-16 for
hypotand 2.2e-16 for theQ-inputsqrt. -
Call-site census (the whole tree, classified by consumption). Divisors — acute:
laplacian._householder_reflector:1861(phase = x0 / modx0) and its twin inmatrix_cascades.qr:206; both were already scale-guarded by rc285, and those guards STAY as defence in depth. Silently-wrong-magnitude:_complex_sqrt_local:1786(a zeromodcorrupts both branches of the complex root),elementwise_transcendental:2700(log(mag)— explicitly guarded against≤ 0, but an inaccurate mag yields a wrong log with no signal), and the publicelementwise_hypot:2836. Comparison guard:ofdm.py:100, which tests|H_k| > 1e-12before dividing bych[k]— the old exact-zero floor could never reach that threshold, but the old inaccuracy band did (2.2e-5 relative at 1e-12), so a borderline subcarrier could be misclassified. All are now exact. -
tests/test_rational_sqrt_relative_precision_rc299.py(52 tests) pins ~1-ulp accuracy across the former underflow band forhypot, theQ-inputsqrt, and the_fhypotprojection the FPU kernels divide by; pins the unit-phase propertyx0/|x0|at seven scales down to2^-100; pins the≥ 1byte-identity; and pins that an explicitprecision_bits=still means absolute. Proof of redness is in-suite and permanent:_sqrt_rational_pre_rc299is a faithful copy of the shipped fixed-kbehaviour, asserted to actually reproduce both the exact0.0and the 44% error, so this ratchet is demonstrably capable of failing.
Unplanned: an rc298 assertion that aborted the process in every assert-enabled build¶
-
Found by running the suite against a locally-built library, which is the only way it surfaces.
srmech_cd_navmap_is_signed_permutationopened withassert(dim <= SRMECH_CD_MAX_DIM)placed above its owncdr_dim_ok(dim)range guard. Its documented contract returnsSRMECH_ERR_BAD_INPUTfor an out-of-rangedim, and rc298's owntest_cd_rungs_rc298.py::test_c_and_python_caps_agreecalls it withCD_MAX_DIM * 2expecting exactly that. Those two cannot both be true: the assert says an out-of-rangedimis impossible, the contract and the test say it is a reported error. With asserts live — the defaultmake libbuild — the assert won and the process tookSIGABRT. -
CI stayed green because the wheel build defines
NDEBUG, which compiles the assert out and lets the error return happen. So the defect was invisible to every gate that has ever run on it, and visible immediately to anyone building the library locally. Under-n autoit did more than fail a test: two xdist workers died mid-run, the loadscope scheduler then raisedKeyError: <WorkerController gw9>, and the session ended at 8794 of ~10150 tests having reported1 failed, 8775 passed— a truncated run wearing a nearly-passing summary. -
Confirmed pre-existing, not introduced here, before touching it:
srmech_cd_register.cis byte-identical toorigin/main,SRMECH_CD_MAX_DIMis unchanged in this rc, and the test dates to rc298 (555f57dbe). The fix moves the assert below the guard, where the bound is genuinely invariant and is what makes theseen[SRMECH_CD_MAX_DIM]extent safe to claim. Both sibling entries in the same file already had that shape, so this was a one-function inconsistency rather than a pattern. Rule-5 assert count is preserved (2), so no JPL ratchet moves.
[0.9.0rc298]¶
Two ceilings that were never where they looked. The Cayley–Dickson rung sweep stopped at 64 because of a stack matrix that addressing never touches; describe() reported 4 classes because it was counting TOML files rather than classes. CD_MAX_DIM 64 → 256, describe()["classes"]["total"] 4 → 5 (+ a new carriers key), describe()["tools"]["total"] stays 466, carriers stay 25, ABI stays 8.
#933 — CD_MAX_DIM 64 → 256¶
-
The trace came first, and it made the rc small. PR #687 named the cap as tooling, not mathematics, and the obvious reading was that lifting it meant confronting the one quadratic buffer in the library —
srmech_sedenion.c'sint64_t mat[MAX*MAX](32 KB at 64, but 524 KB at 256 and 2 MB at 512, against MSVC's 1 MB default thread stack where gcc/clang give ~8 MB). It does not. That buffer lives insrmech_sedenion_is_navigable, the composite-direction reversibility gate. The addressing path the research needs —srmech_cd_navmap/srmech_cd_navigate/srmech_cd_navmap_is_signed_permutation— bottoms out insrmech_cd_basis_productand a linearseen[], and never reaches it. Every buffer addressing does touch is linear in the cap: 2 KB at 256, the largest beinges[]/ei[]at2*MAXints. So the sweep unblocks for the price of a#define. -
The two caps are therefore DECOUPLED, which is the actual change.
SRMECH_CD_MAX_DIM(addressing) goes to 256; the newSRMECH_CD_DENSE_MAX_DIM(the quadratic path) stays at 64. Two sites follow the dense cap: the matrix itself andsrmech_make_class.c's stagingbuf[], which feeds it. -
Stack cost, measured from the built objects rather than estimated (
objdump, x86-64 gcc,-O2):
| frame | prologue | bytes |
|---|---|---|
srmech_sedenion_is_navigable (dense) |
lea -0x8000(%rsp) |
32 KB — unchanged |
srmech_cd_min_generating_set (largest addressing) |
sub $0x1000 |
4 KB |
cd_closure_impl |
sub $0x888 |
2.1 KB |
srmech_cd_navmap_is_signed_permutation |
sub $0x438 |
1.1 KB |
MSVC risk is therefore nil, and that is the point of the decoupling. The deepest frame anywhere in this surface stays 32 KB against MSVC's 1 MB default thread stack — about 3% — exactly what it was before this rc, because the quadratic buffer did not move. Had the two caps been raised together it would have been 512 KB, over half the Windows budget, on every is_navigable call. The addressing path that actually gained four rungs costs 4 KB at its worst.
-
The dense cap is a PERFORMANCE boundary, not a capability one. Past it the C gate returns
SRMECH_ERR_BAD_INPUT, and_native_is_invertibletreats a non-OK return as "route to the exact-rational oracle" — the fallback it already used beyond int64 magnitude.left_mult_is_invertiblestays correct at every dim ≤CD_MAX_DIM, just slower past 64; a test proves it at dim 128 on both a unit and a promoted zero divisor. Per ADR-0009 the capability is the invariant and which projection answers is not. -
Proven at the new rungs, not asserted. rc297 shipped
srmech_cd_navmap_is_signed_permutationso a rung could be checked; rc298 uses it.navmapis a bijection with every sign in{+1,−1}for every direction at 128 and 256, in both projections, with the C peer's verdict cross-checked against Python's. Against the full exact-rationalcd_mult— an independent code path from the cocycle — the cross-check is exhaustive: 4096/4096 pairs at 64 (rc297's number, re-run), 16384/16384 at 128, 65536/65536 at 256. That check isO(dim⁴)(~3 min at 128, ~48 min at 256), so the suite ratchets the complete-but-cheapO(dim²)invariant at every rung plus a deterministic cross-path sample at the new ones, andpython/tools/verify_cd_rungs.py— committed per the computational-provenance discipline — is the generating code behind the exhaustive numbers. -
Where the remaining ceiling actually is. Not memory: the linear buffers would tolerate 1024 at 16 KB. It is verification time — an unproven rung is not a shipped rung, so the cap sits where the proof is still runnable.
-
ABI stays 8, deliberately. No exported signature changed.
srmech_sedenion_is_navigablekeeps(const int64_t*, size_t, int*)and merely narrows the range it accepts — a range it already reported through the existingSRMECH_ERR_BAD_INPUTchannel.SRMECH_CD_MAX_DIMis a compile-time constant, not wire format, and no exported struct is sized by it; an old-header caller passing dim ≤ 64 is bit-for-bit unaffected. AddingSRMECH_CD_DENSE_MAX_DIMis a new macro, not a new symbol. Zero warnings under-DSRMECH_PEDANTIC=ON. -
Holzmann Rule 3, verified against the primary source rather than recalled. The paper (Holzmann, The Power of Ten — Rules for Developing Safety Critical Code, IEEE Computer 39(6), 2006; retrieved from
spinroot.com/gerard/pdf/P10.pdf, the author's own site) states Rule 3 as "Do not use dynamic memory allocation after initialization." The commonly-quoted categorical form drops that qualifier and collapses a two-phase discipline into a blanket ban — incoherently, since the rationale is built around living within "a fixed, pre-allocated area of memory," and establishing that area is itself an allocation. The rationale also explicitly sanctions stack use ("the only way to dynamically claim memory in the absence of memory allocation from the heap is to use stack memory," bounded via Rule 1's no-recursion) and endorses pre-allocated pools. It never mentions fragmentation; that rationale is downstream accretion. srmech has been reading the rule maximally, and keeps doing so — zero malloc in the library at any phase, caller-supplied arenas — which is stricter than both Holzmann and JPL's own F Prime, whose library allocates at init behindFw::MemAllocator. Thetests/test_jpl_audit.pyRule 3 ratchet is unchanged and still down-only. -
What this rc did NOT do, and why — so it is not re-derived later. Two routes exist for lifting the dense cap, and both are correct for a future rc rather than this one, because the trace showed addressing never needed either.
- Capability → the arena. srmech already has
srmech_marshal_arena_t(srmech.h;srmech_marshal_arena_init, consumed bysrmech_make_class.c). Moving the quadratic matrix onto it would make the dimension ceiling caller policy, identical on every platform, with the library still never allocating. That is the route; it needs no new arena concept and must not invent one. - Resource → the PAL. A
srmech_plat_usable_stack_bytes()query (POSIXgetrlimit(RLIMIT_STACK), WindowsGetCurrentThreadStackLimits) fits the PAL's existing shape and would turn a would-be stack overflow — undefined behaviour that may corrupt silently — into an honest refusal, the same inform-don't-crash move astelomere_ticksenescence. - The layering matters and is the reason neither is a shortcut for the other. A PAL-derived cap alone would make dim 256 succeed on Linux and fail on MSVC — the capability becoming a property of where you happen to be running, which is the ADR-0009 defect on a different axis. With an arena in place the PAL question stops being "is this dimension allowed" and becomes "can this host service the arena you asked for": a resource answer, not a capability answer. Note also that
getrlimitreports the limit, not headroom remaining at the call site, and Windows thread stacks are reserved rather than committed — so such a query must document what it actually measures.
#936 — describe() reports capability, not declaration style¶
-
The defect.
describe()["classes"]enumerated the[class]TOML catalog and called it "classes".CDRegister— shipped public class, registered carrier, three C peers — was absent purely because rc297 hand-coded it. A caller asking "what classes do you have?" got an answer shaped by how the classes were written. ADR-0009 mechanism-2: an implementation detail surfaced as the capability. -
The fix is the shape, not the row.
classesnow reports every domain class, with the declaration route as a field (routes: {name: "toml"|"python"}), never as an admission criterion. The TOML-only count is preserved as its own key,toml_total, so no consumer had to keep the old meaning in order to keep the old number. 4 → 5:CDRegisterjoinsGenome/Hurwitz/One/SedenionRegister, andtoml_totalstays 4. -
The set is DERIVED, because a hand-list is what rots into this bug. New
srmech/introspect/_domain_classes.pyunions the TOML catalog with public classes exported fromsrmech.amsc.cascade— the domain-object surface all five live on — so a future domain class is reported the day it ships. The only manual act is the reverse: a value record exported alongside them must be named inNON_DOMAIN_RECORDSwith a reason (currently one entry:Block,One's Hurwitz-block attribute bag). A ratchet fails when a public cascade class is in neither place, so the failure mode is loud over-reporting rather than the silent under-reporting that produced#936. -
CDRegisteris NOT converted to TOML. rc297's reasoning stands: routing both registers through the same machinery would make the dim-16 faithfulness comparison partly circular, since oracle and subject would share failure modes. -
describe()was also blind to the carriers, so that is fixed here too. The 25-entry carrier registry — with a 100% construction-example floor and a compiled-in C peer table — had no representation indescribe()at all.toolsare the verbs andcarriersthe nouns; reporting one without the other described half a package. New top-levelcarriers: {total, names}; per-carrier detail stays incarrier_schema(). The registry already knew about the classdescribe()was missing (CDRegisterhas been a carrier since rc297), which is what made the under-reporting visible. -
dsl.list_classes()is deliberately NOT widened. Three ratchets depend on it meaning exactly "the TOML catalog" — the C class-registry codegen, the registry smoke walk, anddescribe_classcoverage — anddsl.describe_classonly resolves TOML-declared classes. The consumers that compared it todescribe()for equality now assert containment plus an exact match on the toml-routed subset, which is the real invariant: describe() must not lose a TOML class, and itstoml_totalmust agree with the catalog it summarises.srmech_class_registry.cis unchanged in content and correctly still holds 4 — it is the descriptor table, and aNULLfromsrmech_class_descriptor_lookup("CDRegister")is correct rather than a gap; its header comment now says so instead of reading as a package-wide class count. -
Introspection could not report its own dimensional ceiling — new
limitskey.describe()["native"]said whether there was a native library and its ABI, but nothing about what it could do: a probe forCD_MAX_DIM/max_dim/cap/limit/bound/stack/arenaacross the whole blob returned False for every one. A caller — or an LLM driving the MCP surface — could discover the largest admissible dimension only by requesting one and failing, or by reading the C header. For a package whose stance is self-description, that is the same gap as the classes one, a layer down. Now:limits: {"cd_max_dim": 256, "cd_dense_max_dim": 64}. -
limitsreports CAPABILITY only, and the absence of a resource key is the point. The two are different questions and collapsing them would undo the separation the whole dense/addressing split exists to create. - Capability — what this build supports. Invariant across platforms and identical in both projections (the Python constants and the C macros are pinned equal by a test). An artifact property.
- Resource — what this host can service. Varies by platform and thread stack. rc298 measures none, so it publishes none. A compiled constant surfaced under a name implying runtime headroom would be precisely the wrong number this rc line is correcting; a missing key is honest. A test pins the key set and fails if a
stack/headroom/usable-flavoured name is ever smuggled into the capability block. - Deliberately not nested under
native: these ceilings bind the pure-Python path too, and would misdescribe a no-native install. -
When a resource ceiling does arrive it needs a real PAL query, and its docstring must state what the number measures —
getrlimit(RLIMIT_STACK)reports the limit, not headroom at the call site, and Windows thread stacks are reserved rather than committed. -
describe()'s docstring documented six keys and returned seven. Theclasseskey had been undocumented since rc41. All three new keys andclassesare now in theReturnsblock.
[0.9.0rc297]¶
srmech shipped exactly one addressable register, hard-wired to 16 slots — so research that needed 32 had to write its own, and correctly called that a confound. This rc brings the general N-slot register in-tree and proves it faithful against the shipped one. describe()["tools"]["total"] 462 → 466, CEIL_WIRE_GLUE_GAPS stays 10, ABI stays 8 — additive only; no existing op's behavior changes. The ceiling was invisible until the tooling ran on real data.
-
The gap, stated plainly.
SedenionRegisterhard-codes 16:_check_slot()raises outside[0,16)and the literal16appears 11 times in that file. There was no general N-slot register anywhere in srmech. A register's slot count is a Cayley–Dickson algebra dimension andSRMECH_CD_MAX_DIMis already 64, so 32 and 64 slots were buildable today — the cap was never the obstacle.CD_MAX_DIMis NOT raised here (that is task#933, deliberately sequenced after); a test pins it at 64 so the two cannot silently merge. -
The faithfulness gate is stricter than "matches at adequate D", and deliberately so. The obvious gate — reproduce the shipped register 120/120 at
D≥1024— is passed, but it cannot explain the known low-Ddivergence, and a general implementation that agrees only where capacity hides differences leaves its minting unverified exactly where it is exposed. The gate enforced instead:CDRegister(dim=16, namespace="SEDENION")is bit-exact with the shipped class — same recovered key AND same Class-C sign on every one of 120 probes — at everyD, including the starved regime where both fall short. It reproduces the shipped register's116/120atD=256collision-for-collision. -
⚠️ The low-
Ddiscrepancy is EXPLAINED, and it was never a property of either register. The out-of-tree research register read119/120atD=256where the shipped one read116/120, with a different collision pattern. That is entirely the address-name mint: addresses are content-derived asmint_vector(f"{namespace}:e{slot}", D), so"CD16:e0"and"SEDENION:e0"mint different hypervectors and crosstalk differently once capacity is starved. Two facts close it. Bit-exactness above localises the whole difference to one variable. And the ordering REVERSES: atD=256theCD16namespace scores higher, atD=320theSEDENIONnamespace does (120/120vs118/120) — a register that were structurally easier could not lose at lower capacity. A sweep of twelve arbitrary namespaces atD=256spans116..120withSEDENIONinside the spread. So116was one draw from a name-dependent distribution, not a quality ranking, andnamespaceis now a first-class parameter because it is the mechanism. -
Why an N-slot register is legitimate past the Hurwitz wall — shipped as a test, not a comment. Addressing needs no division property; it needs only that basis products be a signed permutation (
e_i·e_j = ±e_k). Zero divisors are built from sums of basis elements, never a single basis pair, so the two properties are disjoint and the boundary that destroys composition leaves addressing untouched (F1274/F1275).cd_navmap_is_signed_permutationmakes that premise checkable at runtime, and the suite enforces it against a full exact-rationalcd_mult— an independent code path from thecd_basis_productcocycle the register uses — at every rung, 4096/4096 basis pairs at dim 64. A companion test pins the contrast it rests on: at dim 32 composition is broken for the majority of generic pairs while addressing is completely intact. -
A real C peer, not a Python-only surface. New
c/src/srmech_cd_register.cshipssrmech_cd_navmap,srmech_cd_navigateandsrmech_cd_navmap_is_signed_permutation, each reachable through_nativedispatch glue (not merely declared insrmech.h—CEIL_WIRE_GLUE_GAPScounts an op only when both hold), and each parity-tested against the pure oracle at dims 4/8/16/32/64. At dim 16 the new symbols are bit-identical to the existingsrmech_sedenion_*peers, which is asserted rather than assumed — a generalisation must not fork the behavior it generalises. ABI stays 8: new plain symbols only, no callback typedef and no wire-format change to any existing export. JPL-clean (≥2 asserts, no goto/malloc/recursion, ≤60-line functions); zero warnings under-DSRMECH_PEDANTIC=ON. Nothing in the file scales quadratically indim, so it imposes no new ceiling onSRMECH_CD_MAX_DIM— relevant to#933, whose binding constraint is thedim²stack matrix insrmech_sedenion.c. -
Hand-coded Python + C, NOT a
make_classTOML — decided, not defaulted.[[feedback_prefer_config_driven_toml_classes]]prefers config-driven[class]TOML, and that preference is overridden here on the record. Themake_classcontract is one-op-per-method plus a singleappends/setsfield;SedenionRegisterwas already assessed HARD at four state fields, andCDRegisteraddsdimandnamespacefor five — strictly worse, and the contract extension is a prerequisite, not a side quest. Independently: the faithfulness gate requires the general register to be an independent implementation of the oracle, and routing both through the same TOML machinery would make the comparison partly circular. The carrier precedent (hand-coded Python + C) applies. -
SedenionRegisterstays an independent class and is NOT collapsed into an n=16 alias. Doing that in the same release that introduces the general register would destroy the reference the gate depends on. A test asserts the two classes are unrelated by inheritance and thatSedenionRegister's source does not mentionCDRegister, so the gate cannot go circular by a later refactor. -
⚠️ Two defects found on the way in, both fixed. (1) Both registers'
navigate()docstrings said the result "shares the codebook";__init__takesdict(codebook), so the mapping is copied — the mintedbytesare shared, but a later write to the parent does not reach the child. Behavior was correct; the wording was not. Corrected in both files and pinned by a test that checks copy-not-alias in both directions. (2) The new suite's own no-abs()guard was written as a regex over module source and failed on the module docstring's sentence sayingabs()must never be used — a text scan cannot tell a call from prose, and would equally have passed a realabs()hidden in a string. Rewritten on the AST, plus a test proving the guard catches a real call and is not fooled by prose. -
Ripple gate applied in full, in order.
_tool_docs.pysettled first (four added lines, no refusal, no collateral — verified bygit diff --numstat, since regenerating registries against unsettled docs bakes a staletool_schema_sha256), then all four generated C registries:toolandcarriermoved;classandresponsioncame back byte-identical, confirmed bygit diff --numstatrather than exit code. The carrier movement is the auto-derived ops back-index picking up the newSequence[int]parameters, not a carrier change. Generated in WSL and CRLF-restored after — the generator writes pure LF over CRLF files..sorebuilt before any test ran.describe()["tools"]["total"]462 → 466 across 56 assertions in 50 modules in seven literal forms. Rosetta: three rows added asc_dispatched, the constructor asnon_compute/composes_cplus a justified entry inCOMPOSES_C_ZERO_REACH_PINNED(it allocates and computes nothing; all compute is in the methods, which route to the three C peers).CEIL_WIRE_GLUE_GAPSunchanged at 10 —cascade.*is outside the wire-format scope set, and the ops have real C peers so they add no gap. All four Rosetta debt ceilings stay 0.
[0.9.0rc296]¶
Two shipped rcs asserted things their evidence did not support. This rc fixes the evidence, not the wording. describe()["tools"]["total"] stays 462, CEIL_WIRE_GLUE_GAPS stays 10, ABI stays 8 — no op is added, removed or changed. What changes is that a down-only ratchet now measures both coherency projections instead of one, and a timing figure with no generating code is struck rather than restated.
-
rc282's syscall ratchet ran the scripting projection ONLY — so its compiled-side claim had zero test coverage. All six tests in
tests/test_genome_read_io_ratchet_rc282.pytook apure_onlyfixture that monkeypatchedhas_native_genome_section_countstoFalse.CEIL_BODY_OPENS_PER_SCANtherefore constrained one projection, while rc282's reported compiled-side result — "measured 4 / 4 / 4 / 4 — constant in P" — was pinned by nothing. The compiled read path could have regressed to per-call re-open, the exact rc280 defect rc282 shipped to fix, with the ratchet still green. Per ADR-0009 the capability is the invariant, so a down-only ceiling that can only ever exercise one projection cannot enforce it: it is a parity gap inside the mechanism built to prevent parity gaps. -
The rc282 number was RIGHT — measured, not assumed. rc296 did not take the 4 on trust. Re-measured with two independent instruments and it reproduces exactly: 4 opens of
turns.binper native scan, flat across the same 25/50/100/200 sweep. The decomposition, which rc282 never stated: 1 Python-side open (section_countsderives the store catalog via_section_entriesbefore dispatching) + 3 C-side; the C library performs 5 read-path opens in total per scan (2manifest.json+ 3turns.bin), also flat in P. Where the correction was needed was the coverage claim, not the arithmetic — so the CHANGELOG correction on the rc282 entry says so plainly rather than implying the figure was wrong. -
Why the gap was structural, and the seam that closes it. Python's
builtins.open/Path.openhooks are blind to anfopeninsidelibsrmech, so no test-side instrumentation could ever have covered the compiled path — the projection was unmeasurable, not merely unmeasured. New internal PAL countersrmech_plat_file_opens/srmech_plat_file_opens_resetcounts every read-path open ATTEMPT the C library makes (file_read/file_read_region/file_open_ro/file_size/rstream_open; writes excluded), which is the same quantitystrace -e trace=openatrecords, so the in-suite ratchet and the committed strace probe measure one thing and are directly comparable. Diagnostic only — no op reads it, nothing branches on it. Additive plain symbols, no new typedef, ABI stays 8; JPL-clean (≥2 asserts, no malloc/goto, ≤60-line functions); zero warnings under-DSRMECH_PEDANTIC=ON. -
Three new ratchet tests, and the
pure_onlyfixture applied only where it does something.CEIL_NATIVE_C_OPENS_PER_SCAN = 5(down-only; the C defect measured 28 / 53 / 103 / 203 over this sweep before rc282) andCEIL_NATIVE_PY_BODY_OPENS_PER_SCAN = 1pin the compiled path's two seams separately, because they have different repair paths; a third test asserts both projections derive identical counts on the same store — the ADR-0009 equivalence rc282 pinned each projection's I/O for but never checked across them. The sweep stores are now built once and shared, so the two projections are measured against the same bytes on disk and a difference between them cannot be a fixture artifact. -
⚠️ The fixture was inert on four of the six tests, which is why it read as a decision.
_catalog_data,_section_node_ids,_read_regionand_read_region_prefixcontain no native dispatch at all — verified by AST walk, not by eye. Forcing pure on those changed nothing, so a blanket application looked like a deliberate per-test scoping judgement that had in fact been made on two tests. It is now applied only where it changes what runs, each genuinely-pure test says why it is pure, and the fixture usesmonkeypatchso a failing assertion cannot leave dispatch globally disabled for the rest of the session. -
Both new ceilings were FALSIFIED before being trusted. A ratchet nobody has watched fail is not evidence. Lowering
CEIL_NATIVE_C_OPENS_PER_SCANto 4 fails on the real observation ({25: 5, 50: 5, 100: 5, 200: 5}); making native dispatch DECLINE at run time — the regression where a "native" test quietly measures the pure path — fires the dead-seam guard (assert min(observed.values()) > 0) with{25: 0, 50: 0, 100: 0, 200: 0}instead of passing vacuously. Every open-count assertion in the file now carries that> 0guard, per[[feedback_false_green_comments_and_dead_instrumentation_seams]]: a seam must fail loudly when it stops observing, not pass with zeros. -
⚠️ rc282's own generating code had not run since rc290.
notes/rc282_c_open_count_probe.sh— the harness rc282 cited as provenance for the compiled-side count — calledsection_counts(d, the_one=one), and the rc290the_one→couplingrename left it raisingTypeError. Provenance that does not execute is not provenance, and a committed harness needs the same rename discipline as shipped code. Repaired, re-run, and extended to print the in-library counter alongside the strace count so the two instruments cross-check: the library's own tally and the kernel's agree on the C-side subtotal. -
rc283's 2.19–2.68× is STRUCK, and no multiple replaces it. The figure had no committed generating code, no artifact and no log — a search of the whole subtree for
2.19/2.52/2.68returns only unrelated May-2026 material. Against[[feedback_computational_provenance_discipline]]that is disqualifying, and the provenance note rc283 carried does not rescue it: acknowledging that a number has no generating code documents that it should not have been published. No portable multiple exists to quote.-n autoresolves toos.cpu_count()(4 on ubuntu-latest / windows-latest, 3 on macos-14), and--dist loadfilecaps the ratio at the slowest single FILE rather than at total work. -
What ships instead: the harness that was missing, reporting a RANGE.
notes/rc283_xdist_speedup_probe.pymeasures serial vs-n autoover N repeats and refuses a single repeat — rc283's own spread (2.19 → 2.68) was attributed to CONTENTION, and a figure whose spread is dominated by what else was running is a property of the afternoon, not of the change. Run on a documented 6-file subset on an 8-core host: 0.96–1.09× over 3 repeats, median 0.99 (artifactnotes/rc283_xdist_speedup.ndjson) — no speedup at all, because one file dominates the set. Same flag, same tree, ~1× instead of ~2.5×, purely from which files you point it at. That run is not a re-measurement of the full suite and is not offered as one; it is the harness proving it executes, and a demonstration of why the struck figure could never have been a property of the change. The CI comment keeps what is checkable —-n autoand--dist loadfilerationale, and the three real order/isolation bugs xdist surfaced. -
Research prose catch-up. Three stale
the_onereferences (a docstring, a comment and aprintstring) inrbs_lm_research/R-RBS-LM-GENOME-BIOLOGY-SURVEY_…_same_and_different.pyfollow the rc290 rename tocoupling.
[0.9.0rc295]¶
The §50 accumulator gets a NON-COLLAPSING read. klein4_bundle_resolve throws the margins away; klein4_bundle_sector_scores returns them. describe()["tools"]["total"] 461 → 462, ABI UNCHANGED at 8 (reasoned below, not left to inference), no existing op's behavior changes and no stored structure changes format. Purely additive to the accumulator family, plus two contract repairs found on the way in.
-
The gap, stated plainly.
klein4_bundle_accumulate(write) andklein4_bundle_resolve(collapsing read) were the entire accumulator family._resolveis a strict per-bit majority: it emits one symbol per coordinate and discards how close the losing sectors were, so a coordinate a sector won 5/9 and one it won 9/9 resolve identically and are thereafter indistinguishable. There was no soft read at all.klein4_bundle_sector_scoresreturns all four per-coordinate sector scores —array('Q'), length4*D, row-majorout[4*i + s]withbit0 = s & 1,bit1 = (s >> 1) & 1— so a caller ranks where the resolved read could only match. -
Why this ships BEFORE F1263's 4xD joint, and the measurement that decided it. Task
#929(commitf5cceb635, harness committed atdocs/srmech/notes/task929_klein4_joint_vs_marginal.py) ran the arm F1263 never ran. F1263 compared exactly two arms — collapsedklein4_bundlevs a full joint count matrix — and reported ~11x recall@1 at N=1200/D=4096. Two things fell out. The 11x does not survive its own sample size: it rested on the baseline scoring 1 hit in 25 probes; at 400 probes the same protocol gives 7.46x. And F1263's stated premise is false —klein4_bundleis not "the argmax read of a count structure", it is a marginal per-bit majority that agrees with the true joint argmax at only 67–69% of coordinates. Splitting "stop collapsing" from "keep the joint" showed the accumulator srmech already ships recovers 4.25x of the 7.46x, and thatjoint_hardis consistently worse than the marginal soft read (0.6075 vs 0.8000 at N=512). The information loss is in the READ, not the STORAGE. So the read ships first — a read change over storage already paid for, rebuilding nothing — and the joint gets priced against what is left rather than against the bundle. -
The remaining gap is real and is NOT dismissed. At N=512/D=4096 the soft read reaches 0.80 against the joint's 0.96. That is a genuine, non-recoverable difference in the usable regime. rc295 makes no claim to close it; it changes what the joint must be priced against.
-
The name had to survive F1259-style regime honesty, so "resolve" was not available. The whole point is that this op does not resolve or collapse — reusing the word would have named an intent the behavior contradicts, which is exactly the defect F1259 names.
sector_scoresstates what comes back: a score per sector, therefore four numbers per coordinate, therefore the uncollapsed regime. A reader at the call site can tellklein4_bundle_resolve(acc)fromklein4_bundle_sector_scores(acc)without opening a docstring. -
It REFINES the hard read rather than replacing it — and that is pinned, not asserted. The agreement product factorises as
a0(bit0) * a1(bit1), so maximising it maximises each bit independently, which is precisely_resolve's strict per-bit majority including its tie → 0 convention.test_argmax_of_the_soft_read_reproduces_resolve_exactlycollapses the margins across seven loads and gets the shipped bundle back bit for bit. Without that, "soft read of the same structure" would be a claim rather than a property. -
Exact integers, and uint64 is load-bearing.
score(s) = a0(s) * a1(s)isn² * P(s)under per-coordinate bit independence — the maximum-likelihood estimate of the joint cell the marginals can support. Ranking is invariant to the1/n², so nothing is ever divided and no float appears. The output is uint64 becausea0 * a1reachesn², which leaves uint32 at n > 65535 folded vectors, and the accumulator carries no such cap; a uint32 result would have wrapped silently on a large store. A malformed accumulator (a 1-count exceedingn) raises rather than wrapping, in both projections. Noabs()— every term is a non-negative count, so no sign boundary arises, and one would be Class-K pin-slot composed with Class-C. -
C peer shipped, reachable, and hand-checked.
srmech_klein4_bundle_sector_scoresinc/src/srmech_hdc.cwith its prototype inc/include/srmech.hand its ctypes binding in_native.py— a symbol the glue cannot reach is not a C peer, so the round trip is exercised, nothasattr-ed. 30 lines, 2 asserts, no goto / malloc / recursion (JPL Rules ⅓/⅘ re-measured on the new function, not inferred from the suite passing). The C smoke inc/test/test_srmech_hdc.cchecks a hand-computed score table against the existing 5-token fixture, including two coordinates whose sector ties the collapsed read cannot express, plus then = 100000 → 10¹⁰uint32-overflow case. -
Re-measured on the NATIVE path, because
#929was not.#929ran pure-Python (HAS_NATIVE=Falsein WSL2) and scored an ad hoc table built inside the measurement script. Generating code + NDJSON committed atdocs/srmech/notes/rc295_klein4_sector_scores_native.{py,ndjson}. Stage 1 rebuilds#929'sm3_marginal_prodtable and asserts the shipped op reproduces it element-for-element — without that, "rc295 delivers the#929lift" would be misattribution — and asserts native == pure. Recalls reproduce#929exactly, which is the expected and correct outcome: the store build and the read dispatch to C, but the recall@1 scoring loop is harness Python on both paths, so matching is what demonstrates the C peer is bit-faithful, not a disappointment. -
Per-dimension, never one scalar (F1264 / PR #687, and
#929's own D=1024 sweep). recall@1, 400 probes, F1263's protocol and seeds:
| D | N | resolve (hard) | sector_scores | joint_hard | joint_soft | shipped read's share of the joint's gain |
|---|---|---|---|---|---|---|
| 1024 | 64 | 0.9062 | 1.0000 | 0.9844 | 1.0000 | 100% |
| 1024 | 256 | 0.2031 | 0.5273 | 0.3750 | 0.7305 | 61% |
| 1024 | 512 | 0.0750 | 0.1650 | 0.1325 | 0.3100 | 38% |
| 1024 | 1200 | 0.0075 | 0.0325 | 0.0250 | 0.0600 | 48% |
| 4096 | 64 | 1.0000 | 1.0000 | 1.0000 | 1.0000 | — (saturated) |
| 4096 | 256 | 0.7891 | 0.9961 | 0.9727 | 0.9961 | 100% |
| 4096 | 512 | 0.3350 | 0.8000 | 0.6075 | 0.9600 | 74% |
| 4096 | 1200 | 0.0600 | 0.2550 | 0.1475 | 0.4475 | 50% |
All eight cells reproduce #929's pure-path numbers exactly, which is the point of running them: the store build and the read dispatch to C, so an exact match is what shows the C peer is bit-faithful. The headline 4.25x is the D=4096 / N=1200 row (0.0600 → 0.2550) — and it is one row of eight, not the op's characteristic number. The same load at D=1024 gives 0.0075 → 0.0325, and the marginals' share of the joint's gain runs 100/61/38/48% at D=1024 against 100/74/50% at D=4096. Do not quote a single lift figure for this op.
joint_hard is below the shipped soft read in every non-saturated cell at both dimensions — "stop collapsing" dominates "keep the joint", which is the finding that ordered this rc ahead of the 4xD structure.
-
Found on the way in #1 — a latent carrier-scan collision that rc295 was the first op to trip.
carrier_schemamatches carrier names against ToolEntry type strings with identifier-boundary lookarounds, and a quote is a boundary — so the stdlib uint64 typecode inarray('Q')matched srmech's exact-rationalQcarrier, and the new op was filed underQ.produces. It produces noQ. Latent rather than benign:Iis the only other typecode in the corpus and no carrier is namedI, so nothing had ever collided. Fixed at the matcher (_strip_array_typecodes) rather than by renaming the op's declared return, becausearray('Q')genuinely is not theQcarrier. The fix is a verified no-op on every pre-rc295 entry — the regenerated carrier registry came back byte-identical to the committed one. Guarded bytest_array_typecode_is_not_read_as_the_Q_carrier, which also asserts a realQproducer is still listed so the check cannot pass by emptying the list. -
Found on the way in #2 — the MCP accumulator round-trip did not close, and two docstrings already said it did.
serialise_nativehad noarray.arraybranch, soklein4_bundle_accumulate(since rc155) and the new op both fell through to_render_result's last-resortreprand crossed the wire as the string"array('I', [...])". Meanwhile the inbound_to_uint32_accdocstring states its input is "the cross-JSON wire form, matchingserialise_native'sarray('I')→list[int]" — describing a branch that did not exist — and_render_resultpromises the two halves are "round-trippable". A repr string does not round-trip, so the accumulator family was the one place both written claims were false. Now emits a flat list, which is exactly what the inbound coercer consumes. This repairsklein4_bundle_accumulate's MCP fidelity as a side effect; shipping the new op degraded to a repr string while a docstring claimed otherwise was not an option. -
ABI: UNCHANGED at 8, by the header's own rule rather than by convenience. The rule bumps on a wire-format change to an existing export, on a new callback typedef (the CFUNCTYPE implication), or on a removal (which is otherwise symptom-free, since the ctypes shim binds by
hasattr). rc295 does none of these: it adds one symbol, introduces no callback typedef, changes no existing signature, and adds no newsrmech_status_tenumerator —SRMECH_OK/SRMECH_ERR_BAD_INPUT/SRMECH_ERR_NULL_ARGare all pre-existing. A stale library simply does not bind the new symbol and the pure-Python projection runs, which is the designed behavior for an additive export. -
Ripple gate applied in full, in order.
_tool_docs.pysettled first (one added line, no refusal, no collateral — verified bygit diff, since regenerating registries against unsettled docs bakes a staletool_schema_sha256), then all four generated C registries:toolmoved;carrier/class/responsioncame back byte-identical, confirmed bygit diff --statrather than exit code. Generated in WSL and CRLF-converted after the Windows interpreter was found to abort on acp1252encode of→— an empty file that a bare>redirect would have committed silently..sorebuilt before any test ran.describe()["tools"]["total"]461 → 462 across 56 assertions in 50 modules in seven literal forms. Rosetta ledger row added asc_dispatched;CEIL_WIRE_GLUE_GAPSunchanged at 10 (the op has a real C peer, so it adds no gap); both annex ratchets untouched by design — ac_dispatchedop moves none of thecomposes_c/host_glue/dev_toolingbuckets.
[0.9.0rc294]¶
An unopenable registry ROOT is an ERROR in both projections. genome_registry stops reporting a typo'd corpus path as an empty corpus. describe()["tools"]["total"] stays 460 (no op added or removed), GENOME_FORMAT_VERSION stays 15, and ABI is UNCHANGED at 8 — reasoned explicitly below rather than left to inference. Behavior change is confined to roots that cannot be opened; an EMPTY root still censuses as n_genomes 0, byte-identically across both projections.
-
The split.
genome_registryanswered two different things on the same input depending on which implementation ran: the scripting projection raisedFileNotFoundErrorout ofPath(root).iterdir(), the compiled one returnedn_genomes: 0withSRMECH_OK. Under ADR-0009 the implementations are co-equal coherency projections of one capability, so there is no "the C is wrong" reading available and no "the Python is wrong" one either — the split itself is the defect. -
The deciding fact was one line, and its comment did not describe it.
c/src/srmech_genome.c:if (st != SRMECH_OK) { *count = 0u; return SRMECH_OK; } /* no root -> none */. The comment claims "no root"; the code means any failure. Everysrmech_plat_dir_openfailure became "0 genomes, success". Verified at runtime against the rc292 build, not inferred from reading: an absent path, a path that is a file (ENOTDIR/ Win32ERROR_DIRECTORY267), and a directory with permission denied all returned{'genomes': [], 'n_genomes': 0, …}with a success status, while the pure path raisedFileNotFoundError/PermissionErrorrespectively. The permission-denied case is the worst of the three: the directory genuinely exists and may genuinely be full of genomes the caller cannot see, and the answer was a confident zero. -
Two independent reasons it was the C that had to move. The sibling contract —
genome_censusandgenome_catalogon an unopenable path already raisedGenomeBoundingError, sogenome_registrywas the outlier in its own family (now pinned bytest_registry_now_matches_its_family). And the docstring never sanctioned it: it promisedn_genomes0 for "a dir with no genome subdirs", which is an empty dir. It said nothing about an absent one, and that promise is unchanged here. -
The fix is NOT
return st, and the reason is a platform seam. Propagating the status would have made the empty-root contract hostage to how each backend reports an empty directory — and Windows genuinely differs. POSIXopendirseparates "opened but empty" from "could not open" natively; Win32FindFirstFilesignals an empty match set withERROR_FILE_NOT_FOUND, indistinguishable at that layer from a failure to open. Most volumes hand back.and..so the wildcard always matches, but a FAT/exFAT root directory has neither, and an empty root there would have started erroring. That trades an ADR-0009 split along a language seam for a fresh one along a platform seam. So the distinction is drawn where the knowledge lives:srmech_plat_dir_opennow returnsSRMECH_OKplus an exhausted iterator for "opened, no entries" on every backend, and non-OK strictly means "could not open".dir_nextreports end-of-directory on that iterator instead of handingNULLtoFindNextFile;dir_closealready tolerated it. -
Verified on Windows natively, not reasoned about. Built with MSVC 2022
/W4 /WXand run on this host: an empty dir →dir_open0 with 2 entries (./.., son_genomes0 holds); an absent dir → 3 (SRMECH_ERR_IO,GetLastError3 =ERROR_PATH_NOT_FOUND); a file as root → 3 (GetLastError267 =ERROR_DIRECTORY). TheERROR_FILE_NOT_FOUNDbranch is unreachable on NTFS by construction, so the state it produces — zeroed handle, no lookahead — was exercised directly instead:dir_nextreturnsst=0 have=0anddir_closereturns 0 without crashing. macOS is the same POSIX source as Linux (__APPLE__maps to theopendirbranch; the only platform-specific quantity issizeof(DIR *), already_Static_assert-guarded), and the Linux run below covers that path — but this is a shared-source argument, not a macOS execution, and the CI matrix is what actually runs clang. -
Both projections now raise the SAME type. Native returns
SRMECH_ERR_IO→GenomeBoundingErrorvia_raise_native_genome; the pure path'sOSErroris caught and re-raised asGenomeBoundingErrorwith the original chained. Leaving the bareOSErrorwould have had the two projections agreeing that this is an error while disagreeing about what kind — the same ADR-0009 defect one layer in. The tests assert type identity, not merely that something raised. -
No test had blessed the defect — and finding that out is itself the result. rc289 met this exact split head-on and deliberately declined to pin either side, leaving a note in
test_absent_path_declines_cleanly_not_by_abortingthatgenome_registrywas excluded because "pinning either side in a test would silently make this rc the decision". That judgement was correct and it held. rc294 is the rc that makes the decision, so the exclusion is discharged rather than deleted:genome_registryjoins the loop and the docstring now records why it was ever absent. -
But the defect HAD generated its own documentation, and the regen exited 0 while preserving it.
_tool_docs.pycarried, forgenome_registry, the executed example{"input": {"root": "'abc'"}, "output": "{'genomes': [], 'n_genomes': 0, 'root': 'abc'}"}— a machine-recorded round-trip that existed only because an unopenable root wrongly succeeded ('abc'is the generator's synthesizedstrargument, and no such directory exists). The first regen after the fix ran clean, reported success, and re-committed that example unchanged, becausegen_tool_docs.pypreserves executed-I/O examples on the rule that they are "not re-derivable" — which is a true statement about derivability and was being used as a false one about truth. Fixed structurally, not by editing the row:_build_examplenow reports whether execution was attempted and failed versus never attempted (carrier/opaque params), and a committed executed-I/O example whose call now raises is dropped in favour of the honest signature snippet. Before this, "failed" and "unverifiable" were indistinguishable at the call site. Executed-I/O count 100 → 99, diff verified to be exactly the one row. -
The generator's refusal was disproved directly, not flag-flipped past. With the guard fixed,
gen_tool_docs.pycorrectly refused to write, naminggenome_registry: example.--accept-seed-driftwas the right branch, established on three points rather than assumed: the field is absent fromCURATED(only itsexplanationis curated, so this is not hand-curation needing migration); it is machine-shaped (_synth_arg("str")returns exactly'abc', matching the recorded input verbatim); and it is no longer reproducible (re-executing raisesGenomeBoundingError). That is the "stale seed" branch the refusal message itself names. Re-running afterwards without the flag writes cleanly, so the guard is back at its healthy fixed point. -
genome_list_chrcarries the same-shaped swallow and was deliberately KEPT — audited, not overlooked. There the swallow does not reach a caller as success:srmech_genome_packturns count 0 intoSRMECH_ERR_BAD_INPUTunconditionally, and the scripting peer does likewise, becausePath.glob("*.chr")also yields nothing on an absent dir and also raises "no .chr files". Both projections already error, so the ADR-0009 invariant holds; changing it would only alter the status a bare-C host sees (BAD_INPUT→IO) while fixing no split. -
ABI: UNCHANGED at 8, and here is the reasoning rather than the conclusion alone. The header's rule bumps on wire-format change, and states that a removal always bumps because a removal produces no other symptom — the ctypes shim binds by
hasattr, so a stale library runs the pure body and nothing catches it. This change adds and removes no symbol, alters no signature, and introduces no newsrmech_status_tenumerator:SRMECH_ERR_IOwas already in this export's documented error set (it is what an unreadableturns.binreturns), so the marshalled wire format is byte-identical. What moves is which condition maps to an existing status — a behavior repair, the same class as rc291's four, which also held at 8. Unlike a removal it is not symptom-free: against a stale library the new parity tests fail loudly, which is precisely the property the removal rule exists to compensate for when it is absent.test_native_status_is_io_not_a_new_enumeratorpins status 3 so that a future change routing this through a new enumerator has to re-answer the question deliberately instead of by omission. -
Ripple gate applied in full: the
tool_schemaToolEntry summary (its "A dir with no genome subdirs yields n_genomes 0" sentence became misleading and was rewritten rather than deleted), all four generated C registries regenerated CRLF-preserved and verified bygit diff --statrather than by exit code (toolmoved;carrier/class/responsionwere already current — no op set change),_tool_docs.pyandtools/gen_tool_docs.pyas above, the public docstring, the C header contract forsrmech_genome_registryand the PAL header contract forsrmech_plat_dir_open(which now states outright that callers must not readSRMECH_ERR_IOas "no entries"), and the rc289 asserts-live module. JPL Rule ⅘ re-measured on every touched function — Windowsdir_openis the longest at 43 lines with 2 asserts, all others ≤ 33; no goto, no malloc. The rosetta ledger row (composes_c), both annex ratchets and the non-compute ratchet are untouched by design: no public callable changed bucket.
[0.9.0rc293]¶
fold_marks — drop combining marks by Unicode CATEGORY, with the decomposition table VENDORED. describe()["tools"]["total"] 460 → 461. ABI is UNCHANGED at 8 — two new exported symbols (srmech_text_fold_marks, srmech_text_default_fold_table), no signature change and no new callback typedef. GENOME_FORMAT_VERSION stays 15. Wire-glue gaps stay 10 (the op ships c_dispatched, so it adds no gap).
-
The name is the contract, and it is why this is not
fold_accents. The downstream name wasfold_accents; a virama is a mark, not an accent, andfold_marks(क्षि) = कषis the case that settles it — the virama (U+094D,Mn) and the vowel sign (U+093F,Mc) are both dropped, and neither is an accent under any reading. The Latin-shaped name would not merely have been imprecise: it would have been wrong in exactly the Indic cases that matter most, while quietly re-scoping the op toward Latin — the assumption this line of work exists to remove. MatchesUPSTREAM_NOTES§106, so the field and the package agree on one name instead of drifting.test_the_op_is_not_named_for_accentsfails if the retired vocabulary returns. -
Scope is category ONLY —
Mn/Mc/Meand nothing else. No case change, no locale tailoring, no NFKD/compatibility folding, no ligature expansion. Several results that read as omissions are the scope working, and each ships with the reason attached:økeeps its stroke (part of the letter, not a mark),ΩOHM SIGN is untouched (a singleton with no mark in it),fi/²/Ⅷare untouched (compatibility, not canonical), and Hangul survives in either normalization form (it decomposes to jamo, which are starters). -
A SEPARATE op, not a mode on
glyph_stream.glyph_streamdocuments and tests a losslessness invariant —''.join(result)reconstructs the NFC-normalised input exactly — and that invariant is why the cluster is trustworthy as a primitive. Folding is lossy, so a fold flag would break the contract for one flag value. The two also have different shapes (str → list[str]vsstr → str), so a caller wanting folded text could not get it from a segmenter flag. Ordinary composition,glyph_stream(fold_marks(s)), keeps both honest;test_glyph_stream_has_no_fold_parameterrecords the decision where a future contributor will meet it. -
VENDORED, and here the argument is stronger than it was at rc287. rc287 vendored UAX #29 because
unicodedataexposes no grapheme-break property at any fidelity — vendored-vs-ABSENT. Hereunicodedatadoes expose category and decomposition, so the scripting projection could derive this at runtime. It must not. The compiled projection has no Python at all (ADR-0003), so a derive-in-Python / vendor-in-C split would put the two co-equal coherency projections on different data — which is precisely the drift ADR-0009 forbids. Deriving in Python would not be a shortcut; it would be the drift. Both projections read the same bytes, and the rc287 consequence follows again: two hosts at different Python / Unicode versions now fold text identically. (This build host runsunicodedata13.0.0 against a table vendored at 16.0.0 — so the host is not, and cannot be, the drift oracle.) -
1,090 ranges / 13,080 bytes, one coalesced table serving both facts in one binary search: payload
0means this codepoint is a mark, drop it; any other value is the codepoint it is replaced by. That is the same merge-into-one-table discipline rc287 used for three UAX #29 property sets. Attested per MPR v1 —UnicodeData.txtUCD 16.0.0,response_sha256ff58e582…, packed-blobtable_sha256437ad841…, re-derivable viac/tools/gen_unicode_fold_tables.py --verify(re-fetches the official file and diffs a fresh derivation against what is vendored). -
The recursion is resolved at GENERATION time, and three invariants are asserted rather than assumed.
ếU+1EBF decomposes to U+00EA + U+0301, and U+00EA decomposes again — the vendored row stores U+0065 directly. So the runtime does no recursion and needs no decomposition buffer, which is what lets the C peer be a flat single-pass loop inside the JPL rules. The generator refuses to emit unless: CLOSURE (no replacement is itself a table entry, so one pass is provably complete and the op is idempotent), ONE-STARTER (a mark-containing canonical decomposition leaves exactly one non-mark codepoint — the flat table depends on it), and NO-GROWTH (folding never widens a codepoint's UTF-8 encoding, which is what makes the C contractout_cap >= text_lensound). All three are re-asserted against the shipped bytes intest_unicode_fold_tables_attested.py, not merely trusted from the generator. -
Needs no normalizer, verified exhaustively. Precomposed characters are handled by the map rows and decomposed sequences by the drop rows, so the same marks fall out whichever form the caller supplies:
NFC(fold(NFC(s))) == NFC(fold(NFD(s)))over the whole codepoint domain, zero violations. The op callsunicodedatanowhere — which is what makes the bare-C host correct rather than approximately correct. The one form-sensitivity is that it PRESERVES the caller's Hangul form rather than composing or decomposing it; it is a fold, not a normalizer. -
Parity is exhaustive, not sampled. All 1,112,064 codepoints run through both projections byte-identically (3,503 of them fold). Sampling would be weak evidence for a table-driven op — the interesting rows are sparse — so the committed test walks every table row's endpoints. A standalone bare-C harness (no Python linked,
srmech_text_default_fold_table→srmech_text_fold_marks) covers the precomposed / decomposed / virama / Hangul / stroke / emoji / empty cases plusSRMECH_ERR_OVERFLOWand malformed-UTF-8 rejection. -
Adjacent fix, disclosed: regenerating the curated docs surfaced that
laplacian.spectral_spine's example output had been stale since rc240 — committed as[2, 0, 1]while native and pure both produce[2, 3, 0]deterministically today. Verified it is a stale doc rather than a native/pure split or platform tie-break (both projections agree; no test pins a literal ordering) and refreshed it. Unrelated to this op, but shipping a documented example the code does not produce is worse than the churn of correcting it.
[0.9.0rc292]¶
Remove hdc.klein4_random. The STOCHASTIC regime is gone from the public surface, with no replacement op by design. describe()["tools"]["total"] 461 → 460; the non_compute ceiling comes back DOWN, host_glue 22 → 21 / total 201 → 200. ABI is UNCHANGED at 8 — srmech_klein4_random was already removed at rc290 (which is what bumped 7 → 8), so this rc removes a Python-only op and touches no exported symbol. GENOME_FORMAT_VERSION stays 15.
-
rc290 closed one of two doors, and this is the other one. rc290 split the Klein-4 mint by regime and removed
seed=fromklein4_random, on the reasoning that a seed made an op named "random" silently deterministic. That reasoning was right and incompletely applied:rng=survived, and a seeded generator through that door is exactly as reproducible. Measured on the rc291 tree —klein4_random(8, rng=random.Random(42))returns[0,0,2,1,1,1,0,0]on both calls. So the op documented with "Correctness criterion: NON-REPRODUCIBILITY" was, through its one remaining parameter, a deterministic minter. -
The survey is what decided it, and it was not close. Across the research corpus that consumes this package, every live
rng=call site passes a seeded generator —default_rng(42),default_rng(token_seed(word)),default_rng(i * 31),default_rng(eigvec_idx * 7919). Not one draws unpredictably. 76 call sites across 25 modules, and zero of them use the regime the op advertises. An op whose declared regime matches none of its actual use is not a regime with a tracked exception; it is F1259 intact, one parameter over. rc290 recorded it as+1 host_glue— a raised down-only ceiling for an op that should not have survived the split. -
No replacement, and that is the design. A caller who wants a genuinely stochastic Klein-4 vector draws their own bytes and calls
klein4_encode_bytes(os.urandom(n), D)— a composition of ops that already have C parity end to end, and one that puts the non-reproducibility at the call site where a reader can see it instead of behind a name in the public surface. Fuzzing helpers belong in tests. Adding aklein4_drawwould just rename the problem. -
Why not a shim. The op is Python-only because its signature takes a caller-supplied Python RNG object — ADR-0009 mechanism-2 exactly, a Python-rooted shape making a parity gap look like a category, with
host_glueas the bucket that laundered it. A compatibility alias would preserve the shape that caused the defect. Breaking is the fix; we are inside one rcN line. -
The
klein4_expandguard no longer advises a removed op. Its non-int-seedTypeErrorused to end "for a genuinely stochastic draw useklein4_random(D, rng=…)" and now names theklein4_encode_bytescomposition. The rc291#921AST ratchet (test_removed_symbols_not_in_messages_rc291.py) is what makes that mechanical rather than remembered, andklein4_randomis added to itsREMOVEDledger. That ratchet's staleness check is also widened to coversrmech.amsc.hdc— it previously introspected onlytextandlaplacian, so anhdcrow would have been recorded and then never validated as genuinely absent. A ledger row that cannot go stale-checked is half a ratchet. -
Two rc290 leftovers found and FIXED, both of which had been silently broken for two rcs.
tests/test_klein4_random_native_rc6.py::test_native_matches_pure_byte_for_bytehad been dead since rc290. It guarded onhasattr(LIB, "srmech_klein4_random")— the symbol rc290 deleted — so it skipped unconditionally, on CI and in the shipped wheel alike; and had it ever run, its body calledhdc._klein4_random_native, which rc290 renamed to_klein4_expand_native, so it would have raisedAttributeError. Two rcs of native-vs-pure byte parity on the expand path were covered by nothing. Retargeted to the real symbol, and the skip is now conditioned onHAS_NATIVEalone with a missing symbol promoted to an assertion — a stale library is a failure, not a reason to skip. This is the "dead instrumentation seam" failure mode: the test was green the whole time because it never ran.-
notes/rc282_c_open_count_probe.shstill calledklein4_random(64, seed=1282)— already broken by rc290'sseed=removal. Repointed toklein4_expand(64, 1282). -
A false claim in the rc290 entry below is corrected in place. It asserted that passing
seed=raised "aTypeErrorcarrying the routing advice"; the actual behavior was Python's bare unexpected-keyword error. The correction is added as a marked note rather than a silent edit. Its two neighbouring claims were re-verified and both hold. -
Full ripple gate applied:
hdc.py(function,__all__, the regime block, the expand cross-reference and guard text), thetool_schemaToolEntry plus the prose inklein4_expand's summary that named it as the per-run alternative (that sentence became false, so it was rewritten rather than deleted), all four generated C registries regenerated CRLF-preserved (toolandcarriermoved — theHVcarrier's derived-ops back-index tracks the op set;classandresponsionwere already current),_tool_docs.pyregenerated (the rc291 generator correctly refused the first run, namingklein4_random's two un-rederivable fields;--accept-seed-driftwas the right branch because the docstring is gone rather than changed, and the resulting diff was verified to be exactly one deletion), the rosetta ledger row and the_rosetta_build_classification.pyroster, the non-compute ratchet and both annex ratchets,test_mcp.py, and thetools.totalpin in 56 assertions across 50 test modules in all seven literal forms it is written in.
[0.9.0rc291]¶
Stop the introspection-docs generator eating curated documentation, and make a removal's stale ADVICE mechanically catchable. Two repairs plus one scoping verdict. No behavior change to any op, no ABI change (7, unchanged), no format bump (GENOME_FORMAT_VERSION stays 15), and no public-callable change — describe()["tools"]["total"] stays 456 — so no ripple gate applied: tool_schema, both registries, the rosetta ledger and the non-compute pins are untouched. _tool_docs.py is touched, but its parsed content is byte-for-byte identical (verified below), so tool_schema_sha256 and srmech_tool_registry.c do not move.
-
tools/gen_tool_docs.pywas destroying curated documentation on every run, and the standing ripple gate told us to run it. Regenerating replaced 20 hand-written explanations with short docstring seeds — four rcs of genome / plasmid / text curation including the native-dispatch notes, the rc280 quadratic-fix explanation, and the facultative-vs-constitutive chromatin read. It had already happened once and been reverted by hand. Root cause, and it is not the one the defect report proposed: the curation-merge layer was never broken or missing. It works exactly as designed —_tool_docs_curated.py(CURATED) is merged OVER the auto-seed — and it was simply bypassed. Those 20 entries were hand-written straight into_tool_docs.py, the GENERATED file, which is stampedDO NOT EDITfor precisely this reason. No marker drifted and no schema orphaned anything; curation was put in the file that gets overwritten. Fix: the 20 entries are migrated intoCURATED, where the merge protects them. Verified as a fixed point — regenerating now reproduces the committed content with zero semantic differences, compared as parsed data across all 456 tools. -
The count in the report was low, and the reason is worth recording. The report said ~40 lines over 12 named ops; it is 40 lines over 20 ops — the six
srmech.amsc.plasmid.*entries andgenome_from_graph/genome_partition/mint_strand/plasmid/glyph_streamwere also being eaten. It read as 12 because a rawdiffon a Windows checkout reports the entire 471-line file as changed: the committed file is CRLF, the generator writes LF, and the real churn only appears once line endings are normalised (git diff --numstatdoes this and shows20 20). Anyone diagnosing this from a raw diff sees either everything or, after eyeballing past it, an undercount. -
On fixing that trap with
.gitattributes— recommended, scoped, and deliberately NOT done here. The repo has no.gitattributesat all and relies on a globalcore.autocrlf=true, so every generator that writesnewline="\n"produces a working tree that disagrees with its own output on Windows. Declaringeol=lfforsrmech/amsc/_tool_docs.pyand_tool_docs_curated.pywould remove the phantom whole-file diff permanently, and it is safe for those two specifically — verified here that no test hashes or byte-compares either file, so nothing re-locks. It is not done in this rc for a reason that is about honesty rather than caution: the same trap applies tosrmech_tool_registry.candsrmech_carrier_registry.c, which are CRLF in the working tree and carry byte-identity + hash ratchets, so any line-ending declaration near them must be proven against those ratchets first. Since a.gitattributesapplies from its directory downward, landing one here would put the registries in scope of a rule this rc has explicitly claimed not to touch. It wants its own change with the ratchets re-run, not a bullet in an rc whose headline is "registries untouched". -
tools/gen_curated_probe.pyhad the same defect, one file over, and worse. It rebuilt_tool_docs_curated.pywholesale from its own hardcodedCENTRALlist, so it deleted every curated entryCENTRALdoes not mention — andCENTRALis a probe list, not the curation SSoT. This had never yet caused visible loss for an exact reason: at rc289CENTRALhappened to cover all 17 curated entries, so the rewrite was accidentally lossless. Migrating 20 more entries in would have made the next run of it destroy them. It now merges over the existing curation and reports preserved-vs-refreshed counts; no key on disk is ever dropped. -
Preservation, and a loud failure when preservation is not possible. The generator now compares the committed file against the fresh auto-seed before writing. Any field that is neither in
CURATEDnor re-derivable from the current docstring is prose it would destroy, so it refuses to write, names the offending keys, and exits non-zero rather than doing it silently;--accept-seed-driftis the escape hatch for the legitimate case where a docstring genuinely changed. Both paths verified end-to-end against a poisoned file — refusal leaves the file untouched, and the escape hatch writes. The comparison is done on parsed data, not text, because the committed file spells non-ASCII prose with\uXXXXescapes while the generator emits literal UTF-8; a byte comparison would fire constantly on a difference that does not matter, and an author who learns to pass--accept-seed-driftreflexively has re-opened the hole. That escape normalisation accounts for 14 lines of this rc's_tool_docs.pydiff; the rest is a rewrittenDO NOT EDITbanner that now names_tool_docs_curated.pyas the file to edit and says plainly what happened between rc274 and rc290 — the header a person is most likely to read immediately before making the mistake. No entry content changed, which is why neither registry moved. -
tests/test_tool_docs_curation_survives_rc291.py(7 tests) pins it, and fails against the pre-rc291 tree for the right reason. The load-bearing one,test_regeneration_is_a_fixed_point, deliberately exercises the real generator rather than this rc's new helper, so it would have failed at rc289 because 20 entries diverge — not because a helper is missing. Confirmed by reverting the curated file and re-running: 5 of 7 fail, including the fixed-point test. -
tokenize()in error strings (#921): already fixed by rc289 — nothing left to change, and the grep is reported in full.text.py:835and:974both nameglyph_stream()at this commit. A full sweep ofsrmech/,c/src/,c/include/andtests/finds 23 livetokenizereferences and every one is legitimate: the rc287 migration notice and the retired-tokenizer prose intext.py, the before/after example in theglyph_streamdocstring, the rc287 notice intool_schema.py, the ABI-7 removal records in_native.pyandsrmech.h, the unrelatedcorpus.tokenizerconfig enum, andimport tokenize— the stdlib module, used fortokenize.open()— in a dozen tests. No symbol namedtokenizeexists in the package. -
The durable part: the removal checklist, made executable rather than written down. The ripple gate follows callers — imports, dispatch tables, registries,
__all__— and a name inside a string literal is not an edge in that graph, which is exactly why a removal can pass every automated check and still leave the package advising a call that cannot be made.tests/test_removed_symbols_not_in_messages_rc291.pywalks the AST of every module and inspects onlyraisesites andwarnings.warncalls — the strings a user is actually shown — asserting none instructs the reader to call a symbol in an explicitREMOVEDledger. Working on the AST is what keeps it from being brittle: docstrings and comments are notraisearguments, so all 23 legitimate history references are excluded structurally rather than by an exception list that would rot. A ledger row is the checklist step; a staleness test keeps the ledger honest by asserting each removed name is genuinely absent. Verified to catch the original defect by restoring the rc287-era strings — 2 of 3 tests fail, then pass again on restore. -
NOT shipped, and NOT forced —
fold_marks(#928 / F1258) is its own rc, materially larger than its report, for a reason the report did not have. The triage is right that FOLD is the one domain-agnostic gap and srmech's to close. Name settled:fold_marks, notfold_accents. The op drops combining marks by Unicode category, and the Devanagari case below is the argument — a virama is a mark, definitively not an accent, and calling it "accent" would have been wrong in exactly the cases that matter most while quietly re-scoping the op toward Latin, which is the assumption this whole line of work exists to remove. It also matchesUPSTREAM_NOTES§106, so the field and the package agree on one name instead of drifting;fold_accentswas the downstream Siona name. Scoping verdict: a separate op, not a mode onglyph_stream.glyph_streamdocuments and tests a losslessness invariant —''.join(result)reconstructs the NFC-normalised input exactly — and that invariant is why the cluster is trustworthy as a primitive; folding is lossy, so a fold mode would break the contract for one flag value. The two also have different shapes (str → list[str]vsstr → str), and a caller wanting folded text could not get it from a segmenter flag. Ordinary composition,glyph_stream(fold_marks(s)), keeps both honest. Scope boundary: drop combining marks by Unicode category only — no case change, no locale tailoring, no NFKD/compatibility folding, no ligature expansion — which is precisely why casefold was declined and is the same lineUPSTREAM_NOTES§106 draws. -
Why it is a full rc rather than an increment. srmech vendors no decomposition data: the only Unicode table in the tree is rc287's grapheme-break table, which packs GBP + Extended_Pictographic + InCB and nothing else. Folding needs canonical decomposition plus
General_Category, and reaching forunicodedatais not open to us — rc287's whole argument is that cluster boundaries are host-version-independent because the table is vendored, and a bare-C host has nounicodedataat all, so that route yields a Python-only public op: an immediate ADR-0009 parity gap againstCEIL_PYTHON_ONLY_DEBT = 0and the down-onlyCEIL_WIRE_GLUE_GAPS = 10. This is measured, not theoretical — the hostunicodedataon this build machine is UCD 13.0.0 against the vendored 16.0.0, three major versions apart, so the two ops would disagree on one tree. Doing it properly means vendoring a fold table +Mnranges fromUnicodeData.txtunder the rc287 precedent (one generator, two projections, MPR attestation with per-fileresponse_sha256, a--verifyre-fetch and a drift test), a C peer, and — because it is a new public callable — thedescribe()["tools"]["total"]456 → 457 sweep across 39 test files. Network retrieval was confirmed working (UCD 16.0.0UnicodeData.txt, HTTP 200, 2,175,362 bytes), so the blocker is scope, not access. -
Where blind folding is WRONG, verified by running it rather than asserted.
क्ष→कष: dropping the virama (Mn) turns one conjunct into two separate letters — it changes the word, not its decoration — while Devanagari matras areMcand survive, so the treatment is inconsistent within one script. Nordicå→abutø→øunchanged, becauseøhas no canonical decomposition; two letters a reader sees as parallel fold differently. Vietnameseế→edestroys vowel quality and tone, collapsing distinct words. Greekϊ→ιremoves a dialytika that exists to disambiguate. Hebrew and Arabic vowel points vanish entirely. So the op is a search/matching projection, not a text normaliser, and must ship documented as one. (Greek uppercase accent loss is the separate #913 and was not touched.)
[0.9.0rc290]¶
BREAKING — the Klein-4 mint is split by REGIME, and the genome coupling becomes a DERIVABLE description instead of an undeclared draw (§102 / F1259 / F1260). Two defects, closed together because they are the same defect at two levels: a value that should have been derived was drawn, and the op it was drawn from could not be read to tell the difference. ABI 7 → 8 (driven by a symbol REMOVAL — see below), describe()["tools"]["total"] 456 → 461, GENOME_FORMAT_VERSION unchanged at 15 but the manifest KEY "the_one" is renamed to "coupling", so existing stores do not load. No shim, no alias, no deprecation path.
-
The honest claim first:
ONE-A14is not better, it is DECLARED. The genome's coupling slot heldklein4_random(dim, seed=<magic int>)— an undeclared draw from an undeclared ensemble (F1259). It now holdshdc.klein4_from_one(one, D): the Class-A content address of theOne's own canonical(σ, θ, terms)serialisation, XORed with the period-14 (1,3,7,3) sector frame. Derivable from three integers — no stored bytes, no seed table, no label. Measured at D=64 over 120 distinct reduced θ (7140 pairs): mean pairwise similarity 0.2501, 0 identical pairs, against the design prototype's 0.2491 and the DRAWN incumbent's 0.2498. Those are statistically indistinguishable and that is the point — you pay nothing for the honesty, and you buy nothing but the honesty. This is not offered as a quality improvement. -
A coupling is a ROLE, not a representation — so the 0.25 floor and incompressibility are the CORRECT targets.
quad_turnapplies the coupling as a uniformklein4_bind, and XOR-by-constant is a Hamming isometry:sim(t₁^c, t₂^c) == sim(t₁, t₂)for anyc. A coupling therefore mathematically cannot transmit structure into stored content, whatever vector occupies the slot — asserted now, not merely argued (test_one_a14_preserves_intra_genome_geometry_exactly). The corollary is counter-intuitive and easy to undo, so it is stated in the docstrings and the header: a structure-preserving coupling is a LEAK. The naive slot projection (one Klein-4 symbol per flat-rational slot, tiled to D) was measured at 0.82 mutual similarity with 1054 outright collisions across 120 θ, and a wrong-coupling cross-read returned the true leaf at 64/64. Do not "improve"klein4_from_onetoward structure-bearing. -
The (1,3,7,3) partition enters as a period-14 sector MASK, because it cannot survive as vector structure. The
One's 14-D adjoint carries only two independent transcendental values plus a sign — ten of fourteen slots are θ-constant, slots 3 and 7 are bothcos θ, slots 4 and 12 both±sin θ. So the partition is operator structure (howG(σ,θ)acts) while any projection target is an operand. As a mask it is well-defined at every D, which is exactly why nothing here requires 14-divisibility. New public ophdc.klein4_sector_frame(D)exposes it, which makes the claim falsifiable rather than decorative: XOR the frame back off and the raw Class-A expansion reappears exactly. -
Honest disclosure carried in the code, not just here: the sector frame is STATISTICALLY INERT. By the same isometry, masking cannot move a pairwise statistic — measured, masked and unmasked distributions are identical (
test_sector_frame_is_statistically_inert_and_says_soasserts the equality, so if the frame ever stops being a constant the docstring becomes false and the test fails). It is carried for structural legibility and attestation, and it is labelled that way in the docstring, thesrmech.hblock and the tool schema. -
NULL RESULT recorded, not omitted — D-divisibility by 14 earns nothing (falsifier F7 → FALSE). 14 = 2×7 and 7 never divides 2ⁿ, so no power of two is ever divisible by 14. Measured across 56/64, 112/128, 224/256, 448/512, 896/1024: every matched pair agrees to ~0.001. The dominant effect is D itself (larger D tightens concentration), not divisibility. D is not changed, and the reason is structural rather than lucky: the working projection never tiles the partition into D positions.
-
ONE-D4(the base-4 digit ladder of the One's own rationals) was REJECTED and must not be resurrected. It passes on a spread θ grid and fails adversarially when θ clusters in value: 0.2747 → 0.3336 → 0.4477 at denominators 10² / 10³ / 10⁵, becausecosis continuous so nearby θ share leading base-4 digits (at θ = 10⁻⁶ the first 20 of 40cosdigits are identical). A coupling with a hidden precondition on the caller's θ distribution is the wrong shape. A content address has no continuity in θ, which is whyONE-A14holds the floor on that same row (0.248 / 0.2532 / 0.2523) — pinned bytest_one_a14_holds_the_floor_when_theta_clusters_in_value.
The regime split — four ops where there was one¶
One op served four regimes with different correctness criteria, and the call site never declared which. A seed= parameter spanning both "magic number" and "content address" is precisely what let DRAWN and DERIVED be conflated, because a reader could not tell them apart at the call site. Separate ops make the wrong choice hard to write rather than merely explicit; each docstring states its regime and its correctness criterion, which is the actual defect being fixed.
| op | regime | correctness criterion |
|---|---|---|
hdc.klein4_expand(D, seed) |
EXPAND — what the old op was on every seed= call |
reproducibility |
hdc.klein4_random(D, rng=…) |
STOCHASTIC — narrowed; the one regime where "random" is true | NON-reproducibility |
hdc.klein4_address(D, content) |
ADDRESSED — Class-A content address | identity + structurelessness |
hdc.klein4_role(D, role, base) |
ROLE — a binding key for a named slot | near-orthogonality between roles |
-
The teaching case, in the docstrings.
klein4_addressof"cat"/"cats"scores 0.2589 against a 0.2454"cat"/"dog"control at D=8192 — the one-character edit is invisible.klein4_encode_bytesscores 0.6597 on the same pair, because it composes position-bound per-byte vectors. SHA-256 avalanche flips ~48.8 % of output bits per one-character edit: high diffusion is exactly what makes a good ADDRESS and exactly what disqualifies it as a REPRESENTATION. Same property, two opposite requirements. That is the whole reason these are separate ops rather than a documented convention. -
klein4_randomlostseed=outright, and that removal IS the fix. Keeping it would reinstate the defect: an op named "random" that a seed makes silently deterministic, so a call site cannot be read to find out which regime it is in. Passing one is now aTypeError, not a silent regime switch.klein4_expandlikewise refuses bytes andklein4_addressrefuses an integer, each naming the op the caller wanted.
Correction (rc292). As originally written this bullet said the
seed=TypeErrorcarried the routing advice. It did not. Removing the parameter left Python's own bareklein4_random() got an unexpected keyword argument 'seed', with no mention ofklein4_expandor any other op — the routing-advice guard the sentence was describing belongs toklein4_expand, which refuses a non-int seed, and is a different guard for a different mistake. The two neighbouring claims in this bullet were re-verified at rc292 against shipped behavior and both hold:klein4_expand(D, b"…")andklein4_address(D, 42)each raise aTypeErrornaming the op the caller wanted. Only theklein4_randomsentence was wrong, and it is corrected here rather than deleted so the record shows what was claimed. rc292 removes the op entirely, which moots the guard question.
- Every deterministic call site moved, value-identically.
klein4_expand's MT19937 stream is byte-for-byte the CPythonrandom.Random(seed).randrange(4)sequence the old op produced — asserted againstrandom.Randomdirectly, so the rename is proved not to have moved a byte. The §60 byte vocab / position keys, the RBS-LM substrate atoms,cooccurrence_fold's token codebook andfold_encode's role/value codebook all repoint; the last two becomeklein4_role, which is exactlyklein4_expandover the same_cooc_token_seedfold and is asserted to be so.
C parity, ABI, and the one op that is deliberately Python-only¶
-
ADR-0009 — both coherency projections, byte-identical, no exceptions. New C symbols
srmech_klein4_address,srmech_klein4_role,srmech_klein4_sector_frame,srmech_klein4_from_one, plussrmech_klein4_expand. Native and pure are asserted byte-identical at D ∈ {1, 7, 13, 14, 15, 64, 65, 128, 1000} — deliberately including D not divisible by 14 — over content from empty to 10 KiB, and the C peer declines to the pure path rather than truncate a θ past the int64 wire (a truncation would make the projections disagree while both reported success). -
klein4_addressis TWO-STAGE, and the second stage is not decoration.h = sha256(content), thensha256(h | i)fori = 0,1,2,…. Folding to a digest first is what lets the C counter loop run out of a fixed buffer for content of any size. The naive one-stage form would need the whole preimage resident per block, which under JPL Rule 3 (no malloc) forces a compiled-in ceiling — i.e. exactly the "it declines cleanly above N" shape ADR-0006 §2.5 names as an anti-pattern and ADR-0009 generalises from carriers to implementations. Two stages, no ceiling, no decline, no arena. -
ABI 7 → 8, driven by a REMOVAL — stated, not assumed.
srmech_klein4_randomis removed, andsrmech_klein4_expand,srmech_klein4_address,srmech_klein4_role,srmech_klein4_sector_frameandsrmech_klein4_from_oneare added. Per the policy note insrmech.h(made explicit at rc287 precisely because its absence let a removal ship unbumped), removing an exported symbol always bumps, and the five added symbols alone would not have. The rename was not cosmetic tidying: with akeysupplied the C function is a pure deterministic expansion, so a bare-C host reading "random" got the sharper form of the lie — it has no other regime to fall back into. Leaving the C name wrong while fixing the Python one would have re-created the Python-rooted taxonomy ADR-0009 exists to name. A dedicated test asserts the new symbols are present, the old one is absent, and both ABI numbers read 8 — because a stale library is caught by nothing else (the ctypes shim binds byhasattr, so the wrapper would quietly run its pure body while every other op dispatched into a mismatched build). -
klein4_randomis Python-only, by REGIME rather than by debt — named here so it does not pass silently. It is classifiednon_compute/host_gluein the Rosetta ledger (host I/O; tracked, no ceiling), notpython_only_debt, and no debt ceiling moved. The reasoning: its output is by definition not a function of any input, so there is no kernel to mirror and nothing to differentially test for byte-identity — two implementations of "unpredictable" cannot be compared. A bare-C host needing an unpredictable Klein-4 vector reads its own entropy source, exactly as this reads Python's. The capability every cascade actually consumes — deterministic Klein-4 minting — is fully covered in both projections byklein4_expand/_address/_role/_from_one. All five deterministic ops arec_dispatched— includingklein4_role, and closing that one was a real catch rather than a formality. It was first classifiedcomposition_of_con the standingcooccurrence_foldprecedent, but a check of the C tree found no peer for the FNV-1a_cooc_token_seedfold it composes over (srmech_rbs_lm.c'stoken_seedis a different, SHA-256-based derivation, andsrmech_klein4_cooccurrence_foldtakes codes the host has already computed). So the composition bottomed out in Python and the bucket would have been a comfortable fiction on a NEW public op — exactly the shape ADR-0009 exists to catch.srmech_klein4_rolecloses it in ~20 lines and is asserted byte-identical to both the pure path and the pre-rc290 seeded call, socooccurrence_foldandfold_encodeare unchanged.
BREAKING — the genome's the_one= is now coupling=¶
-
Two unrelated objects named
the_onehad already misled a reader, andONE-A14makes the old name more misleading rather than less: the slot now holds something derived from theOnebut not theOne.cascade.the_one(the 14-D exact-rationalS(σ,θ)) is untouched and keeps its name. Every genome parameter, internal helper (_default_the_one→_default_coupling,_the_one_block_bytes→_coupling_block_bytes,_resolve_the_one→_resolve_coupling,_the_one_bytes_or_empty→_coupling_bytes_or_empty), C parameter and C static (genome_build_the_one→genome_build_coupling,the_one_len→coupling_len) moves in lockstep. No alias — a caller passingthe_one=gets aTypeError, which is asserted. -
The manifest key moves with it, which is a STORE-BREAKING change.
manifest.json's"the_one"object is now"coupling", renamed in the Python builder and the C JSON builder/parser together so the C/Python manifest mirror stays byte-identical (both emit sorted keys, so the alphabetical position moves consistently on both sides).GENOME_FORMAT_VERSIONstays 15 because no byte layout changed — but every existing store must be re-saved, exactly as with the rc287 vocabulary break.
Ripple gate and verification¶
- Full gate applied:
tool_schemaToolEntries (5 added,klein4_random's rewritten to dropseed),_tool_docs.pyentries hand-applied and diffed (its generator was NOT run — it overwrites curated genome explanations with auto-seeds), bothsrmech_tool_registry.candsrmech_carrier_registry.cregenerated CRLF-preserved, the.sorebuilt from this worktree and confirmed withnm -D, Rosetta ledger rows added, thetools.totalpin updated in 39 test files, andtest_mcp.pyupdated. The carrier registry moved even though no carrier changed — theHVcarrier's derived-ops back-index picked up the five new ops, which is the documented way a parameter-type addition shifts it. - A new MCP coercer was required:
klein4_from_onedeclares aOneparam, andtest_all_param_types_json_coerciblecorrectly failed until one existed. It is theOne's own canonical{"sigma", "theta", "terms"}dict round-trip (one_from_jsonable) — no new wire form is invented, and a float θ component is rejected rather than truncated. - Tests rewritten because they passed only on the old naming, named explicitly:
test_mcp.py::test_klein4_random_seed_reproducible→test_klein4_expand_seed_reproducible(the BUG-A capability is unchanged; the op serving it is renamed — the rc13 fix that addedseed=toklein4_randomdelivered the capability and created F1259's defect);test_random_ops_rng_takes_precedence_over_seed(klein4 has no seed to take precedence over — it now asserts theTypeErrorand that two draws differ, with the precedence check kept onpolar_random, where it still applies);test_random_ops_schema_drops_unserialisable_rng(retargeted toklein4_expand);test_invoke_klein4_random_seed_reproducible_rc14_path→..._expand_.... Coverage went up, not down: three new MCP tests and a 30-testtests/test_klein4_regime_split_rc290.py. -
Generating code for every figure quoted above is committed at
docs/srmech/notes/rc290_one_a14_shipped_measure.pywith its NDJSON output alongside. It asserts its own resolved import path — a script undernotes/puts its own directory onsys.path[0], which silently loaded a stale user-site install during the design work, so it now fails loudly rather than measuring the wrong package. -
The ripple gate is wider than the documented list, and the extra step was found by a FAILING TEST rather than by the checklist.
srmech_class_registry.cis a THIRD generated C table (beside the tool and carrier registries) and it bakes the raw[class]TOML descriptor bytes — so renaming the genome'sthe_onefield inclass_catalog/genome.tomlsilently desynchronised it, andrun_class_methodstopped dispatching to C for every Genome method while the pure path kept answering correctly. That is the ADR-0009 failure shape exactly: one projection working and the other quietly not, with no error anywhere. Regenerated and rebuilt; the 100 tests intest_run_class_method_c_rc202.py/test_make_class_engine_c_rc201*.pyare what caught it.srmech_responsion_registry.cwas then checked the same way and regenerates byte-identically (itsthe_onerows are the CASCADE One, correctly untouched). -
The v2 genome fixture was MIGRATED, not regenerated, and the distinction is the point.
tests/data/genome_v2_fixture/manifest.jsonis a real pre-rc290 store, so the manifest-key rename broke it exactly as it breaks a user's. It was migrated the way a user migrates one — the JSON key moved and nothing else did.turns.binis byte-identical,format_versionis still 2, and all three digests (data.body_sha256,attestation.response_sha256, and the coupling block's ownsha256) are unchanged, because each hashes the BODY or the coupling BLOCK and none of them hashes a key name. So what that module actually tests — that the v2 BIT-PACKED BODY still reads leaf-for-leaf — is untouched by the rename. The reasoning is recorded in the test file, not only here, so the next reader does not have to infer why a fixture was edited. -
Pins moved, and each is named rather than quietly bumped. ABI
== 7→== 8across 12 test modules;tools.total/len(schema.tools)456→461across 49 modules in two different literal forms (["tools"]["total"] == Nandlen(...) == N— the second form is invisible to a grep for the first, which is how a stale pin has broken CI on this project before); and the non-compute ratchet re-pinnedhost_glue21 → 22 / total 200 → 201 in three modules with the rationale inline. No DEBT ceiling moved and none was touched —python_only_debtandc_exists_unboundboth remain 0, becauseklein4_randomis Python-only by REGIME, not by debt.
[0.9.0rc289]¶
Fix the genome_list_genomes assert that ABORTED the host on a documented supported input, and give JPL Rule 5 a configuration where it means something. rc288 found this while verifying and deliberately left it for a C rc (see its entry below); this is that rc. No ABI change (7, unchanged — a bug fix inside an existing function, no signature touched), no format bump (GENOME_FORMAT_VERSION stays 15), no public-callable change (describe()["tools"]["total"] stays 456), so no ripple gate applied — tool_schema, _tool_docs.py, both registries, the rosetta ledger and the non-compute pins are all untouched, and _tool_docs.py's generator was not run.
-
The abort, and the contract it was asserting wrongly.
c/src/srmech_genome.c:3535, ingenome_list_genomes, assertednames == NULL || max_n > 0u. The registry's own two-pass caller (srmech_genome_registry) reaches pass 2 withnames != NULL, max_n == 0whenever pass 1 counted no genomes — it deliberately allocatesmax(n,1)slots precisely so the zero case has a valid buffer. That is an EMPTY ROOT, a documented supported input whose docstring promisesn_genomes: 0, and whose own C comment says "A missing root yields count 0 (not an error)". The settled contract:max_nis a CAPACITY and zero is a legal capacity. The assert was wrong, not the code — and the decisive evidence is that the code below it was already correct: then >= max_noverflow guard fires before any store, sonamesis never dereferenced at capacity 0, and a Release build of the unmodified source returns exactly the right answer. An assert that fires on an input its own function handles correctly is a false constraint, so it was replaced rather than pushed onto the caller (assert(names != NULL || max_n == 0u)— the count-only pass must not claim capacity it has no buffer for, while the fill pass may legally carry capacity 0). Assert count per function is unchanged at 2; the JPL ratchet is untouched. -
Why it was severe rather than cosmetic, and why nothing caught it. In both projections on the same input: the scripting one returns
{'root': …, 'n_genomes': 0, 'genomes': []}; the compiled one terminated the process (SIGABRT, exit 134). Not a wrong value and not a clean decline — one projection answers correctly while the other kills the host, which a bare-C host cannot even trap. It was invisible because every shipped build and every CI cell compiles-DNDEBUG, which stripsassertoutright (docs/srmech/CMakeLists.txtnotes this in three places). Pre-existing, and not from the rc282–rc288 chain:git log -L 3535,3535:…/srmech_genome.cputs its introduction in rc267 (bd4ff5e13, the §96 census/registry ship — the same commit that added the empty-root test it breaks). -
A
MISSINGroot aborted on the same assert too, not only an empty one. The assert sits at function entry, so it fires before thesrmech_plat_dir_openfailure path that was written to handle exactly that case. Both inputs are fixed by the same change. -
The regression is a parity assertion, not "C stopped crashing."
tests/test_asserts_live_smoke_rc289.pypins native and pure to the same documented shape for an empty root, because the scripting projection was already correct and a test pinning only the compiled one could pass while the two still disagreed. Against the pre-rc289 tree it does not fail — it aborts at exit 134 and pytest prints no report at all; reaching its assertions is the fix. A populated-root control is included so a capacity-0 fix that broke real scanning could not hide behind an all-empty expectation. Note thattests/test_genome_census_rc267.py::test_registry_empty_rootalready covered this input since rc267 and passed throughout — the missing thing was never the test, it was a build configuration that could see the abort. -
New CI cell
asserts-live-smoke(JPL Rule 5 with teeth). Before this, the≥2 asserts per non-exempt functiondiscipline thattests/test_jpl_audit.pyratchets down-only bought nothing on any path CI exercised — we ship-DNDEBUG, so every one of those asserts was decorative in the only configuration that ran. The cell builds the C library-DCMAKE_BUILD_TYPE=Debug, swaps it into the source tree (cwd-wins, per rc224), verifies mechanically that__assert_failis actually referenced before running anything — a green result from a silently-stripped build proves nothing — and runs the smoke serially (never under--dist loadfile, where a dying worker destroys the report before the summary prints). The smoke module re-checks the same thing from Python underSRMECH_ASSERTS_LIVE=1, so a misconfigured cell fails loudly instead of passing quietly. -
That cell's first CI run FAILED — on its own guard — and that is the result it was written to produce. The build-type override was not reaching CMake, so the cell installed a Release library and ran the smoke against stripped asserts. Six of the seven edge-case tests passed happily; only
test_asserts_are_actually_liveobjected. Without that guard the cell would have reported 8 passed in 0.28s — green, fast and entirely vacuous — and we would have shipped a permanent no-op that certifies nothing. Root cause:pyproject.tomlpins[tool.scikit-build] cmake.build-type = "Release", and scikit-build-core does not read a bareCMAKE_BUILD_TYPEenv var, so a separatecmake -DCMAKE_BUILD_TYPE=Debugbeside the install had no bearing on the library the install produced. Fix: pass it through the backend —pip install -e ".[dev]" --config-settings=cmake.build-type=Debug, which outranks the pyproject value — and delete the build-and-copy step entirely. The old approach depended on a copied.sowinning asrmech.__path__race against the installed one; measured in a venv reproduction, that race goes different ways in different environments (the source tree wins only when it actually has a_native/directory, otherwise_find_library()resolves the site-packages copy). Building the installed library Debug removes the race rather than trying to win it. Verified by replaying the corrected job from a clean venv (8 passed,__assert_failpresent in the resolved library) and by a negative control that reinstalls without the config-setting and reproduces CI's1 failed, 7 passedexactly — the guard still has teeth; the build was fixed, not the check. The 7 edge-case tests were then re-confirmed under a genuinely asserts-live install, along with the three previously-aborting modules (10 / 74 / 9 passed). Note also that the earlier local verification never usedpip installat all — it ran fromcwdwith a hand-placed library, which is why it could not have caught this; the smoke module now documents the install command so the local and CI paths agree. -
Scope of that cell, and what a full asserts-live run actually surfaced. Measured for this rc rather than assumed: all 483 test files were run under an asserts-live build, each in its own process so an abort is recorded rather than aborting the session. It surfaced one root-cause assert, reaching three modules —
test_genome_census_rc267,test_mcp(viamcp/_tools.invoke_tool) andtest_immolation(same route) — and no other aborts anywhere. There is no backlog of pre-existing aborts to work through, so the cell runs a targeted smoke over documented edge-case inputs (empty directory, empty genome,n == 0, zero-length label, absent path) rather than a second full ~12–14 min suite re-proving a null. Widening it is a one-line selection change. No assert was weakened to make anything pass. -
NOT fixed here, and NOT decided here —
genome_registryon an ABSENT root is a separate ADR-0009 split. With the abort gone, the compiled projection returnsn_genomes: 0for a path that does not exist while the scripting projection raisesFileNotFoundError; before this rc the same input produced a third behaviour (abort). This is pre-existing and independent of the assert. Deciding it is a semantic change to a public surface with a migration cost, so it is reported rather than settled, and no test here pins either side — pinning one would silently make this rc the decision. The evidence, for whoever takes it: the sibling family (genome_census,genome_catalog) raises on an absent path in both projections; the publicgenome_registrydocstring promises0only for "a dir with no genome subdirs", i.e. an existing directory; and returning "empty cell" for a mistyped or unmounted root converts a caller's error into a plausible-looking answer. That points at raise in both, but it is a direction call, not a bug fix. -
tokenize()→glyph_stream()in shipped error strings, plus three stale references the ripple gate cannot see. rc287 removedtokenize;srmech/amsc/text.py:835(cooccurrence_topk) and:974(thecooccurrence_edgesnormaliser) still told callers to "tokenize()it first", naming a function that no longer exists. Both now nameglyph_stream(), verified to be the correct advice for those call paths rather than substituted blindly. A sweep of the package and the C tree for the string found three more, all present-tense descriptions of the current pipeline and all fixed as comment text only: thetext.pymodule docstring (text → tokenize → …),c/src/srmech_text.c:6,9, andc/include/srmech.h:11295,11297(comment-only — no prototype, no ABI implication). Deliberately left as accurate history: the rc287 BREAKING migration notice intext.py, the ABI-7 removal record in_native.py/srmech.h, the datedc/ROSETTA_LEDGER.mdentries, and the unrelatedcorpus.tokenizerconfig enum inadapters/substrate_parameterization.py. No symbol namedtokenizeexists anywhere in the package — nothing defined, exported, or registered intool_schema, the C tool registry or the rosetta ledger. The ripple gate covers callers, not references, which is why these survived it.
[0.9.0rc288]¶
Documentation catch-up for the rc254–rc287 span — the BREAKING glyph-stream tokenizer, the genome storage format, the BREAKING vocabulary rename, §101 progress/abort, a mat_eigvals correctness advisory, and an honest C-host coverage statement. The PyPI long-description last caught up at rc253 (b8d1350f3, the #1390 family). Since then the genome/storage family shipped an entire surface the README never named — it had exactly one genome-adjacent mention, and that was the directed Class-L recovery family inside the laplacian row — and rc287 changed what every text-derived store contains. No behavior change in this rc; docs + version only. No new tool, no C symbol, no format bump: ABI is 7 (rc287's removal took it 6 → 7), GENOME_FORMAT_VERSION stays 15, describe()["tools"]["total"] stays 456. Every literal below was read out of the tree at this commit, not copied from a prior CHANGELOG entry.
-
⚠️ New README section
srmech.amsc.text— the glyph stream, and the rc287 BREAKING change given top billing. This is the change with the widest blast radius in the span and the README had no text section at all, so it gets both a banner in the intro (above the fold, where a reader upgrading will hit it) and a full section.tokenize/DEFAULT_STOPLISTremoved,glyph_stream(text, *, unicode_normalize=True)in their place, no shim. A before/after table states the six things that actually changed for a caller (unit, casefold, length floor, stoplist, stored-data validity, ABI), and the point that matters most is stated plainly: the container is fine and its contents are not —GENOME_FORMAT_VERSIONdoes not move because no byte layout changed, only which strings went into it, so every pre-rc287 vocabulary / edge store / text-built genome must be re-encoded. Why the word was the wrong unit is given with its measured consequence (~89% of CJK/Thai types singletons vs ~20% English; a co-occurrence graph over an 89%-singleton vocabulary carries almost no association mass) rather than asserted. The vendored UAX #29 table is documented as 683 ranges / 6,147 bytes, UCD 16.0.0, MPR-attested and content-addressed — both figures verified by computation here (len(GB_TABLE_BLOB) == 6147;6147 / 9 == 683exactly), not read off the constant. Conformance 1093/1093 on Unicode's ownGraphemeBreakTest.txt, and — unlike the prior verification pass, which ran withHAS_NATIVE=Falseand skipped two tests — this one was run against a freshly builtlibsrmech.sowithhas_native_text_glyph_stream()true, so the 9/9 pass covers both coherency projections, which is what "in both implementations" is supposed to mean. -
⚠️ New README correctness advisory —
mat_eigvalsreturned a WRONG spectrum before rc285. Placed directly under the 14-class table, because a reader who computed eigenvalues on an earlier release needs to recompute them and will not find that out from a version number. States the mechanism (the Householder reduction to Hessenberg form was entirely absent, so shifted-QR iterated on an unreduced matrix; and the reflector divided by ahypotcascade whose inaccurate-nonzero returns broke the similarity property) and, more usefully, the trigger: vertex labelling, not hub dominance. That distinction is the whole reason it survived — it is not a pathological-input bug. A 4-node path relabelled0-2-1-3returned[1, 1, 1, 3]against a true[0, 2−√2, 2, 2+√2]. Relabelling cannot change a spectrum, so an answer that moves under relabelling is wrong on its face; that invariant is now a 65-test ratchet across all six shipped eigensolvers, named individually, not only the one that broke. The 65 was confirmed by real--collect-onlycollection. -
New README paragraph — the rc282 genome read path, including the part that is still not bounded. One held handle per scan replaces the per-region re-open in both projections (a
_body_handlecontextmanager on the scripting side; a heldFILE*plus a new platform read-at trio on the compiled side), and catalog derivation streams instead of slurping, bounded by the largest single region — one region rather than one block because the SHA-256 op has no streaming API. Two limits are stated with it rather than left to be discovered. (a) The ratchet that pins this (CEIL_BODY_OPENS_PER_SCAN = 2, over a 25/50/100/200 sweep, with an independent assertion that 8× more sections must not raise the count at all) covers the scripting-coherency implementation only — it deliberately disables native dispatch, and there is no C-side syscall assertion anywhere in the suite. The compiled projection's measured open count is a commit-message figure from an uncommittedstraceprobe, so the README does not quote it as ratcheted, because it is not. (b) The bound is on RAM, not bytes read:gene_express_plan's call-level I/O is not bounded as its docstrings once advertised and cannot be while the chromosome table is derived by scanning (ADR-0003 forbids storing it) — call-level bytes-read is ≥ the whole body; only the per-region gate reads are bounded. -
rc283 (pytest-xdist) is a CHANGELOG line and deliberately gets no README prose. It is CI-runtime only —
pytest tests/ -q -n auto --dist loadfilein the workflow,pytest-xdist>=3.5in both pyprojects, and no API change (no file undersrmech/changed beyond the version bump). None of that is visible to somebody installing from PyPI, and the README is the long-description. Recorded here for completeness: three order/isolation bugs that serial execution had been hiding (a collection-timeos.urandomin aparametrizethat made workers disagree on the collected set; aclear_registry()process-global wipe with no restore that passed serially only on alphabetical luck; and a~/.srmech/cross-worker singleton whose dead-endpoint sweep unlinked another worker's in-flight socket). Provenance note: those timing figures had no committed generating code or measurement artifact — they existed only as prose in a CI comment. Against[[feedback_computational_provenance_discipline]]that is a real gap, and it is why they were stated here as "what its own three runs measured" rather than as a package property.
Correction (rc296). The speedup figures are struck, not restated. The sentence above originally read "measured 2.19–2.68× across its own three runs — explicitly not the 2.92× an earlier scoping investigation reported, which this rc did not reproduce". A provenance note acknowledging that a number has no generating code does not make it citable; it documents that it should not have been published. rc296 searched the whole subtree for
2.19/2.52/2.68and every hit is unrelated May-2026 material — there was no harness to re-run. No multiple replaces them, because the quantity is not a package property:-n autoresolves toos.cpu_count()(4 on ubuntu-latest / windows-latest, 3 on macos-14, 8 on the host that measured this), and--dist loadfilecaps the achievable ratio at the slowest single FILE rather than at total work. rc296 commits the harness that was missing (notes/rc283_xdist_speedup_probe.py) — it reports a range over repeats and refuses to run a single repeat, because rc283's own spread was attributed to CONTENTION, and a figure whose spread is dominated by what else was running is a property of the afternoon. Run on a documented 6-file subset on an 8-core host it measures 0.96–1.09× over 3 repeats (median 0.99; artifactnotes/rc283_xdist_speedup.ndjson) — no speedup at all, because one file dominates the set. Same flag, same tree, ~1× instead of ~2.5×, purely from which files you point it at. That subset run is not a re-measurement of the full suite and is not offered as one; it is the harness proving it works, and a demonstration of why the original figure could not have been quoted as a property of the change. What survives unstruck is the durable, checkable content: xdist's adoption and the three order/isolation bugs it surfaced.
-
C-host coverage section updated to the shipped state:
CEIL_WIRE_GLUE_GAPS11 → 10. The count was read out oftests/test_rosetta_transitive_standalone.pyon the rebased tree, not taken from the brief that asked for the edit. Thelaplacian.recursive_cutrow is deleted — rc284 landedsrmech_laplacian_recursive_cutand it left the allowlist. The replacement prose says plainly that unblocking is not closing:recursive_cutwas the shared dead-end ofgenome_from_graphandgenome_partition, so closing it unblocked both, and each still needs a C surface of its own (genome_partition: exact-integer participation, antimode histogram, per-node classify;genome_from_graph: all of that plus the in-RAM subgraph relabel, the per-groupgraph_to_kernel→mint_strandloop, and strand assembly). Thegenome_from_graphgap-reason was corrected accordingly — it no longer listsrecursive_cutas a blocker. -
gh #907folded in — the README's "stdlib only" claim was not true on Python 3.10, and it is now stated exactly.pip install srmechadvertised "stdlib only, no numpy, ever" whilepyproject.tomldeclarestomli>=2.0; python_version<'3.11'— a genuine third-party runtime dependency, because TOML parsing only entered the stdlib astomllibin 3.11. The install line drops the false half and keeps the true one ("no numpy, ever"), and a new paragraph names the dependency, the exact version condition, and the seven modules carrying thetomllib/tomliimport pair:amsc/catalog.py,amsc/descriptor.py,amsc/tool_schema.py,dsl/_catalog.py,dsl/_class_catalog.py,dsl/_toml_chain.py,profile_loader.py. (The issue text lists six;dsl/_toml_chain.pyis the seventh, found by sweeping the tree.) It is named as the self-hosting gap it is rather than a packaging detail, becausec/src/srmech_toml.cships a 1,485-line TOML parser for exactly the bare-C host ADR-0003 targets and the Python implementation does not read TOML through it. Documented, not fixed — the fix is a code change and does not belong in a docs rc. -
Found while verifying, NOT fixed here —
genome_registryaborts the process on an EMPTY directory in any assert-enabled build. Surfaced by running the suite against a locally builtlibsrmech.so(plainmake, soassertis live).srmech.amsc.genome.genome_registry(root)on a directory containing no genome subdirs tripssrc/srmech_genome.c:3535,genome_list_genomes: Assertion 'names == NULL || max_n > 0u' failed— aSIGABRT, not a raised error. The count-only pass is contracted to passnames == NULL; a zero-genome directory instead reaches it withnames != NULLandmax_n == 0. This is a documented supported input —genome_registry's own docstring says "A dir with no genome subdirs yieldsn_genomes0" — so the supported path is the one that aborts. It is invisible to CI because the wheel builds Release, anddocs/srmech/CMakeLists.txtnotes in three places that "Release/NDEBUG strips assert"; it is invisible to the pure-wheel cell because the native branch is guarded byhas_native_genome_registry(). It is therefore also an ADR-0009 parity break, and of the worst shape: on the same input the scripting projection returns{'root': …, 'n_genomes': 0, 'genomes': []}— exactly what the docstring promises — while the compiled projection raisesSIGABRT. Parity means byte-identical results; one projection cannot answer while the other kills the process. Verified both ways here. Reachable in practice throughmcp/_tools.invoke_tool, which is howtests/test_mcp.py::test_every_advertised_tool_invocablehit it. Pre-existing onorigin/main— this branch does not touchsrmech_genome.c(its only C diff is the two version defines insrmech.h) — and needs a C rc with its own regression test, not a docs rc. -
Found while verifying, NOT fixed here — two shipped error strings still point at the deleted
tokenize.srmech/amsc/text.py:835(incooccurrence_topk) and:974(in thecooccurrence_edgesnormaliser) both raise"…not a raw str — tokenize() it first", and the module docstring at:4still describes the pipeline astext → tokenize → cooccurrence_edges → dense_laplacian. A caller who follows that error message calls a function rc287 removed. It is cosmetic rather than behavioral — the guard itself is correct, only its advice is stale — but it now directly contradicts this README, which documentstokenizeas gone. Left for a source rc rather than fixed here, on the same reasoning as#907: this rc changes documentation, and a docs rc that quietly editssrmech/stops being reviewable as one. -
rc286 was skipped and is not documented as shipped. It claimed ABI 7 but never shipped; rc287 supersedes it completely (rc286 parameterised the
_MIN_LENmachinery rc287 deletes outright), so ABI 7 belongs to rc287. The numbering gap is deliberate and is already recorded inc/include/srmech.h. -
New README section
srmech.amsc.genome/srmech.amsc.plasmid— the genome storage format. Wire format v15, the cap-marker table (CHROM0x43/ diploid0x44/ kernel telomere0x6B/ active telomere0x74/ centromere0x58/ chromatin0x48/ gene0x47+ the four promoter markers), the op-family table, and the two-stage encode. Also added as surface 6 in the intro list, which previously enumerated five. -
The rc271 BREAKING rename is given its own flagged subsection.
"stick"→"plasmid","minted"→"nuclear"("diploid"unchanged), with the point a reader upgrading actually needs stated up front: this changes derived STRINGS, not bytes — no on-disk migration, no format or ABI bump — and the value-alias opt-in (set_type_aliases/clear_type_aliases/load_type_aliases_toml) is named with a worked round-trip. -
Newly documented beyond the rename: centromere / diploid /
mint_strand; the chromatin access gate and its rc274 cell-state-conditional form; copy-number (amplify/copy_number_of) including then == 1byte-identity; the rc273integratecompatibility gate; the census/registry family; the data-structural partitioner;graph_to_kernel/kernel_to_graph; and the §101 progress + graceful-abort primitive (rc275) — which had zero mentions despite bumping ABI 5 → 6. -
⚠️ New README section "C-host coverage — what a bare-C host cannot run today", and the parity overclaim it replaces. The header paragraph asserted "Everything mirrors — a bare-C host runs the whole apparatus" and "the orchestration has a 1:1 C peer". That is the exact claim-shape ADR-0009 §1.2 was written to stop, in the package's own front door. It is now a capability statement (two co-equal implementations, ADR-0009 vocabulary: scripting-coherency / compiled-coherency, no primary) plus a link to an enumerated gap list — the
CEIL_WIRE_GLUE_GAPSdown-only allowlist read fromtests/test_rosetta_transitive_standalone.py, 10 at this commit, named op by op. The rc280section_countsdecline bounds (~11,000 sections / 196,608 ids against a 240,881-section corpus, and non-reentrancy) are stated in the same place. Framed as coverage, with no date and no roadmap promise. -
ADR-0009 vocabulary sweep across the README (the §6 mechanism-1 follow-on, applied to this file only): "Python fallback" / "falls back to pure Python" / "the pure-Python fallback is used" → the Python implementation servicing the call; "native dispatch" kept where it describes routing and explicitly marked as not evidence of parity.
-
Stale literals corrected — every one verified against the tree, not the CHANGELOG. The
native_status()example printedabi_version: 5, expected_abi: 5and a barenative_version: '0.9.0'; the liveEXPECTED_ABI_VERSIONis 7 after rc287, and the example now shows the real triple this build reports. The tool registry was described as~400-entry;describe()["tools"]["total"]is 456. The cascade catalog was10 descriptors; the directory holds 15.signal_processingwas40 ops (38 + pi_cascade + rfft); the module exports 41 — the enumerated list in the same paragraph already had 41, andifftwas the one missing from the prose count.chiral_dual's C entry was marked "queued";srmech_cascade_chiral_dual_f64has been declared inc/include/srmech.hsince v0.4.5rc8. The MPR example'sparser_versionreadsrmech 0.8.0. The cascade-catalog line claimed "full C/Python parity per project discipline" as a blanket property; it now states what is true of that catalog. -
Every code example in the new sections was executed against this tree and its real output pasted, and re-run after the rebase against a freshly built
libsrmech.so(numpy-absent,HAS_NATIVE=True, ABI 7 — the earlier pass ran on the pure body, so this one additionally proves the examples hold when native dispatch is live): the census round-trip ({'plasmid': 1, 'nuclear': 1, 'diploid': 0},nuclear-like), the alias round-trip,amplify/copy_number_of(12 1, equal strand length,n == 1byte-identity), the fouraccessiblereads across cell states, the progress tick sequence[(3, 0, 3), (3, 1, 3), (3, 2, 3)]and the 1-chromosome cancelled partial, andconserved_corein both its derived (k = 2) and policy outcomes. The new glyph-stream examples were executed the same way, and thetokenize→glyph_streamchange broke none of the existing six, because none of them touched the text surface.
[0.9.0rc287]¶
⚠️ BREAKING — the tokenizer's unit is now the GLYPH CLUSTER, not the word. srmech.amsc.text.tokenize and DEFAULT_STOPLIST are removed, replaced by glyph_stream. There is no shim, no legacy_mode=, and no parallel old path: per user direction, "breaking means fixing." Every stored vocabulary, co-occurrence edge store and text-built genome is invalid and must be re-encoded (GENOME_FORMAT_VERSION is unchanged — the container is fine, its contents are not; ABI bumps 6 → 7 — the C change is one symbol out and two in, and the REMOVAL is what forces it). Design note: docs/srmech/notes/glyph_stream_tokenizer_design.md.
-
What the word decision was actually doing.
tokenizekept runs of Unicode letter|mark codepoints, casefolded them, dropped anything under 2 codepoints, and filtered against 146 English function words — as the default, for every language. Measured on real Wikipedia prose (18 languages, 365,177 chars), that front door did not merely mis-segment non-Latin text, it manufactured a degenerate vocabulary: scriptio-continua scripts collapsed into single tokens up to 96 characters long, leaving ~89% of Chinese/Japanese/Thai types as singletons (English: 19.6%). A co-occurrence graph over an 89%-singleton vocabulary carries almost no association mass. It also deleted content outright —tokenize("中 国")returned[](both single-codepoint CJK words gone to_MIN_LEN),tokenize("’okina")returned["okina"](U+2019 was mapped to ASCII'then stripped word-initially, so the Hawaiian okina vanished), and every emoji sequence returned[]while1️⃣returned a token made only of combining marks with no base character. F1257 had separately found that the operator layer IS the conserved core (94/94 tokens entering it were stoplist members), so the default was discarding precisely the layer the science found load-bearing. -
A grapheme cluster is well-defined in every script, which is the whole argument — there is no per-language word decision to get wrong.
glyph_streamNFC-normalises, then segments per UAX #29. No casefold, no stoplist, no length floor; whitespace and punctuation are clusters because they are clusters. The op is lossless:"".join(result)reconstructs the normalised input exactly, so a boundary can never fall inside a codepoint or between a base and its marks (design falsifier F7 / R-RBS-LM-25 §3.5). Case folding and confusable normalisation are per-locale concerns that now belong downstream, not at the front door. -
Why the UAX #29 tables are VENDORED, and why the aggregate error figure must never be quoted alone. A best-effort
unicodedata-only derivation (Hangul by syllable algebra, RI by block, Control/Extend/SpacingMark by category) scores 954/1093 on the official conformance suite. On real prose its boundary error is:
| script | error |
|---|---|
| Latin, Greek, Cyrillic, Arabic, Hebrew, CJK, Korean, Hawaiian | 0.0000% — exact |
| Lao / Thai | 0.71% / 0.83% |
| Devanagari | 7.96% |
| Bengali | 9.23% |
| Burmese | 19.16% |
| aggregate | 0.75% |
That 0.75% aggregate is misleading and is recorded here only beside the per-script rows. The derivation is exactly correct on the scripts that barely need clustering and badly wrong on the ones that need it most — because Brahmic scripts are precisely where a "character" is most strongly not a codepoint. Shipping the approximation would have rebuilt the English-privilege problem in a new place, one aggregate number deep. The decisive point is narrower still: Extended_Pictographic (GB11) and InCB (GB9c) are not exposed by unicodedata at any fidelity, so the real choice was vendored-vs-ABSENT, not vendored-vs-derived, and absent means broken emoji and broken Indic.
-
UAX #29 needs THREE upstream files, not one —
GraphemeBreakProperty.txt(GB3–GB13),emoji-data.txtforExtended_Pictographic(GB11), andDerivedCoreProperties.txtforInCB(GB9c, added in Unicode 15.1). Omitting InCB alone scores 1086/1093; those 7 cases are the only signal GB9c exists, and they are how the third dependency was found at design time. -
The vendored artefact: 683 ranges / 6,147 bytes (
c/src/srmech_unicode_gb_tables.h+python/srmech/amsc/_unicode_gb_tables.py, both emitted byc/tools/gen_unicode_gb_tables.py). That is 35% under the design's 1,051-range / 9,459-byte projection, because the three property sets are merged into ONE coalesced range table rather than three — overlapping rows collapse, and lookup is one binary search instead of three. Packed byte: gbp in bits 0-3,Extended_Pictographicin bit 4,InCBin bits 5-6. -
Hangul by arithmetic — but only the half that is actually arithmetic. The precomposed syllable block U+AC00..U+D7A3 is omitted from the table and recovered by the UAX #29 §3 algebra (
(cp − SBase) % TCount), saving exactly 798 ranges / 7,254 bytes. Jamo L/V/T are kept as table rows:LBase/VBase/TBaseare composition anchors that do not coincide with the GBP jamo ranges — U+1160 HANGUL JUNGSEONG FILLER is GBP=V yet sits below VBase=U+1161, and Jamo Extended-A/B lie outside them entirely. Deriving jamo from those constants cost 4 conformance cases (1089/1093, caught by<U+1100, U+1160>needing GB6) and would have saved only ~8 ranges. This corrects a conflation the design note carried. -
Attestation (MPR v1), and a re-derivation path that actually runs. Each table carries a full attestation block in the shape of the
srmech_sha256_constants.hprecedent: source URLs, Unicode version 16.0.0,retrieved_at, theresponse_sha256of each upstream file, and the sha256 of the packed blob. All four upstream digests were re-fetched and verified byte-for-byte against the design ledger.gen_unicode_gb_tables.py --verifyre-fetches the official files, recomputes the table and diffs it against what is vendored, exiting non-zero on any drift — a vendored table nobody can re-derive is exactly the failure the MPM discipline exists to prevent. -
The drift guard the design proposed would have been RED on this project's own build host, so it was not implemented. The design note called for "a committed test that re-derives the vendored table's derivable subset from the running
unicodedataand fails on divergence". Measured against a host atunidata_version 13.0.0with the table at UCD 16.0.0, that test reports 4,140 mismatches across 6,469 derivable rows — of which 3,982 (96%) are simply codepoints unassigned in the host's older Unicode, and only 158 are genuine reclassifications. It would go red whenever a host's Unicode predates the vendored UCD, which is the ordinary case; a guard that is red for a legitimate reason is a guard people learn to ignore. Replaced by three host-independent layers intests/test_unicode_gb_tables_attested.py: the packed blob's attested sha256 (a hand edit the generator did not bless fails immediately), byte-identity between the C and Python tables, and functional conformance. Upstream drift stays where it belongs — in--verify, which needs network and is therefore a script, keeping the annual re-vendoring cost visible rather than silent. -
A property that runs the OTHER way from the usual vendoring worry. Because the table no longer tracks the host, two hosts at different Python/Unicode versions now segment text identically. The retired derive-from-host tokenizer could not promise that — it achieved native==pure parity by construction on any one host, while quietly allowing two hosts to disagree. This closes the design's Unknown U4 (real-world UCD version skew, which the spike measured on a single matched-version interpreter and explicitly could not test).
-
Conformance: 1093/1093 in BOTH coherency projections, with zero disagreement between them (
tests/test_glyph_stream_conformance_rc287.py, against the attestedGraphemeBreakTest.txtfixture whose own sha256 is asserted). This closes design falsifier F6 — "both projections agree byte-identically" — which the spike recorded as untested, no implementation exists yet, and which is the gate this rc existed to pass. -
Both projections, capability first (ADR-0009). The capability is segment text into UAX #29 extended grapheme clusters over the full Unicode domain, given a break-property table. Scripting projection:
srmech.amsc.text.glyph_stream. Compiled projection:srmech_text_glyph_stream— a single forward pass over a 5-field state record, with GB9c and GB11 (both specified with lookbehind) folded into running flags so there is no lookbehind buffer, no scratch arena and no file-scope state; the op is reentrant and the table is a caller-provided input.srmech_text_default_gb_table()hands a bare-C host with no Python present the vendored default, satisfying ADR-0003 concretely rather than in principle. JPL-clean: ≤60-line functions, ≥2 asserts each, no goto/malloc/recursion; pedantic build (-Werror) clean. This also resolves the design's Unknown #3, which flagged GB9c/GB11 lookbehind as plausibly fitting Rule 4 but unverified. -
srmech_text_tokenizeis REMOVED from the C surface, keeping C and Python 1:1 — the rc135 carrier-consolidation precedent (an orphaned kernel with no caller is removed, not left dangling). -
⚠️
SRMECH_ABI_VERSION6 → 7 — the first bump this project has made for a REMOVAL rather than a callback typedef. Every prior bump (v2–v6) was driven by a new function-pointer typedef and its CFUNCTYPE implication; the header's own policy note said only "adding a NEW symbol does not bump ABI; changing an existing signature does" and never mentioned removal at all, which is precisely how this shipped unbumped through a first review pass. The policy line is now amended insrmech.hto say so explicitly.
The reason is not the one it first appears to be, and the difference was worth checking. The tempting argument is that a stale ABI-6 .so would leave the caller with word-segmented output while Python believed it was glyph-streaming. That does not happen, and it was tested rather than assumed: with the library loaded but srmech_text_glyph_stream absent, glyph_stream() runs its pure body and returns a correct grapheme-cluster stream — conformance still 1093/1093. The hasattr binding degrades safely for that one op.
The actual hazard is that a removal produces no symptom at all. It raises nothing (the shim binds by hasattr), it returns nothing wrong (the pure body is correct), and the ABI check passes — so HAS_NATIVE stays true and every other op in the library keeps dispatching into a build compiled from different source. A signature change would at least corrupt its own call. A removal is quieter than that: it silences the mismatch signal rather than the output. The ABI version is the only mechanism left that can catch the pairing, which is exactly why removal has to bump it — and exactly why the header's policy note, which mentioned only additions and signature changes, needed amending. Updated in lockstep: EXPECTED_ABI_VERSION in the ctypes shim, plus 18 pinned assertion sites across 12 test files (NATIVE_ABI_VERSION / EXPECTED_ABI_VERSION / test_introspect.py's differently-shaped status["expected_abi"], which a literal grep found and a symbol grep would have missed — a stale pin of exactly that kind has broken CI on all four cells twice on this project). The long-stale test_abi_version_is_5 (which asserted 6) is renamed test_abi_version_is_7.
rc286 also claimed ABI 7 and is not shipping — rc287 supersedes it completely, since it parameterised the _MIN_LEN machinery this rc deletes outright — so 7 belongs here. The rc286 branch and its findings are retained and the numbering gap is deliberate; renumbering was considered and rejected as more expensive than it looks.
- Scale, measured against the design's projections on the same 18-language corpus — and it reproduces almost exactly:
| tokens | types | tok/type | edges | density | |
|---|---|---|---|---|---|
| WORD | 51,787 | 20,775 | 2.5 | 199,306 | 0.00092 |
| GLYPH | 345,983 | 4,294 | 80.6 | 118,769 | 0.01289 |
Ratios glyph/word: types 0.207× (projected 0.207×), tokens 6.68× (projected 6.68×), edges 0.596× (projected 0.596×). The naive worry that glyph granularity explodes the store is false: the stream is 6.7× longer but the vocabulary shrinks ~5× and edges ~40%, because the graph is bounded by vocabulary, not stream length. Density rises 14×, which is the point — association mass concentrates instead of scattering across singletons.
Two honest caveats. (1) These ratios are corpus-scale-dependent and invert at small N: on a 6,756-character sample the glyph stream had 2.4× more types and 4.2× more edges, because word vocabulary grows with corpus size (Heaps' law) while the glyph alphabet saturates. The "~5× smaller vocabulary" claim is a corpus-scale claim and should not be quoted for short texts. (2) Segmentation time came in at 0.95× the word tokenizer — i.e. marginally faster, against a projected 3.41× slower. The design measured the pure path with a dict lookup and flagged (Unknown #8) that a packed-range binary search "would change it, likely favourably"; with the C peer and the packed table it does, decisively.
-
What is NOT in this rc, deliberately. Derived units are not implemented. The design tested Harris branching entropy and found real signal (2.0–2.7× over a frequency-matched random baseline) but F1 ≈ 0.55 with precision < 0.52 — more than half of proposed boundaries wrong. That is a research finding, not a segmenter, and it stays an open fermata rather than shipping as an unproven surface. The glyph stream stands on the conformance and scale evidence alone.
-
The co-occurrence window is now in the wrong units, and this rc does not silently rescale it.
window=5used to span ~5 words; it now spans ~5 glyphs (under one word). Window semantics need re-deriving per corpus, not multiplying by 6.68. Flagged in thetextmodule docstring and in the rewritten round-trip test. -
Tests rewritten, not deleted. Every assertion in
test_text_graph_u1_rc43.pyandtest_text_c_rc217.pythat passed because of word-segmentation assumptions was rewritten to state the corrected claim, with the old claim named in the docstring. Three of them were pinning bugs as contract: the_MIN_LENfloor, the apostrophe trim that deleted the okina, and the stoplist that discarded the conserved core. One test (test_tokenize_fold_outputs_carry_no_apostrophe) guarded a genuine invariant of the retired fold-then-trim C path whose consumer no longer exists; it is replaced by the structural counterpart — that the vendored table crosses the FFI boundary intact and still carries the two non-derivable properties. New suites:test_glyph_stream_conformance_rc287.py(the 1093-case gate + both projections + differential),test_glyph_stream_multiscript_rc287.py(70 cases across English/Turkish/Greek/Hawaiian/Bislama/Arabic/Hebrew/Devanagari/Thai/Chinese/Japanese/Korean + emoji ZWJ, skin tone, flag parity and keycaps, each run in both projections),test_unicode_gb_tables_attested.py. -
Ripple gate (the public callable surface changed; count is NEUTRAL).
ToolEntrysrmech.amsc.text.tokenize→srmech.amsc.text.glyph_stream, sotools.totalstays 456 and no pinned count-test moved. Regenerated:srmech/amsc/_tool_docs.py(viatools/gen_tool_docs.py),c/src/srmech_tool_registry.candc/src/srmech_carrier_registry.c(both CRLF preserved; the C tool-schema byte-identity hash-ratchet re-locks). Rosetta ledger row →srmech.amsc.text.glyph_stream, bucketc_dispatched._native.pybinds the two new symbols andhas_native_text_tokenize→has_native_text_glyph_stream.test_mcp.py,test_tool_registry_c_rc184.py,test_carrier_schema_rc205.py,test_rosetta_completeness.py,test_rosetta_transitive_standalone.py,test_jpl_audit.pyandtest_non_compute_ratchet_rc170.pyall green.CEIL_NON_COMPUTE_OWEDstays 0. - Ripple gate (the public callable surface changed; count is NEUTRAL).
ToolEntrysrmech.amsc.text.tokenize→srmech.amsc.text.glyph_stream, sotools.totalstays 456 and no pinned count-test moved.srmech/amsc/_tool_docs.pywas hand-edited, deliberately NOT regenerated —tools/gen_tool_docs.pydoes not merge with curated entries, it overwrites them with shorter auto-seeds, and a regeneration run for this rc silently destroyed curated documentation for four separate rcs' work (accessible's facultative-heterochromatin/Barr-body explanation,integrate's compatibility-gate and rc276 C-peer paragraph, the rc281 native-dispatch notes onamplify/copy_number_of, and the rc280 quadratic-fix explanation onplasmid_extract/section_counts, among others). That was caught in review and reverted; the file now differs from its pre-rc287 state by exactly one line, theglyph_streamentry. The generator's broken curated-merge is filed separately as its own defect. Regenerated:c/src/srmech_tool_registry.candc/src/srmech_carrier_registry.c(both CRLF preserved; the C tool-schema byte-identity hash-ratchet re-locks). Rosetta ledger row →srmech.amsc.text.glyph_stream, bucketc_dispatched._native.pybinds the two new symbols andhas_native_text_tokenize→has_native_text_glyph_stream.test_mcp.py,test_tool_registry_c_rc184.py,test_carrier_schema_rc205.py,test_rosetta_completeness.py,test_rosetta_transitive_standalone.py,test_jpl_audit.pyandtest_non_compute_ratchet_rc170.pyall green.CEIL_NON_COMPUTE_OWEDstays 0.
[0.9.0rc285]¶
#1440 — mat_eigvals returned a WRONG spectrum, and the one-line invariant that would have caught it is now a ratchet over every eigensolver. A shipped-mathematics correctness fix. mat_eigvals reported [2−√3, 1, 1, 2+√3] for the star K(1,3) Laplacian whose true spectrum is [0, 1, 1, 4] — trace preserved, interior exact, extreme pair keeping its correct sum while splitting about its mean incorrectly. Three distinct defects were found; #1440 reported one of them, and its diagnosis of that one was wrong.
- Defect 1 — the Hessenberg reduction was missing entirely (the reported bug).
mat_eigvalsis a shifted-QR iteration, not a Jacobi sweep, so the issue's "unfinished 2×2 Jacobi rotation on the hub row" reading does not describe it. The sweep's deflation test reads the single subdiagonalH[m-1][m-2]and acceptsH[m-1][m-1]as converged — sound only for an upper-Hessenberg matrix, where that entry is the whole of the last row below the diagonal. The Householder reduction to Hessenberg form (Golub & Van Loan §7.4.3), which §7.5's practical QR algorithm assumes throughout, was simply absent. On an unreduced matrix withH[m-1][m-2] == 0but a non-negligibleH[m-1][j],j < m-2, the sweep deflates a non-eigenvalue and then solves the wrong leading block. New_hessenberg_complexsupplies it.
The trigger is not hub-dominance. It is H[n-1][n-2] == 0 — the last two vertices being non-adjacent. A star hits it because its leaves are pairwise non-adjacent, but so does a path under an unlucky labelling: dense_laplacian(4, [(0,2),(2,1),(1,3)]) returned [1, 1, 1, 3] against a true [0, 2−√2, 2, 2+√2]. The defect was label-order dependent, which is why P3/P4/C4/K4 spot-checks all passed.
The sharpest demonstration: one graph, two labellings, two different wrong answers. #1440's edge list [(0,1),(1,2),(1,3)] puts the hub at vertex 1 and yields 2∓√3 (0.267949 / 3.732051 — the issue's numbers); the same star with the hub at vertex 0, [(0,1),(0,2),(0,3)], yields 2∓√2 (0.585786 / 3.414214), from a leading block whose determinant is 2 instead of 1. Both are wrong; neither is 0. This is why the ratchet's strongest property is relabelling invariance rather than the star special case.
- Defect 2 —
_fhypotis nothypot, and the Householder reflectors were not reflectors._fhypotis a bounded-denominator Class-N rational cascade: it carries ≈ −2e−5 relative error at1e-12and returns exactly0.0below ≈1e-17. Sophase = x0 / _fhypot(x0)was not unit-modulus for a smallx0(measured 1.25 atx0 = 6.9e-17), hence|α| ≠ ‖x‖andP = I − β·v·vᴴwas not a reflector. The Hessenberg reduction was therefore not a similarity: measured 1.6e-1 asymmetry from a symmetric input and 1.4e-2 of spectral drift on an 11-vertex broom graph. New_householder_reflectordivides the column by its largest component magnitude first (Pis scale-invariant), keeping every_fhypotcall inside its accurate range, and falls back to the real phase branch when|x0|is negligible against‖x‖— where the phase is round-off noise and there is no cancellation to avoid. Both_hessenberg_complexand_qr_complex_listnow build reflectors through it.
This one was found by the new ratchet, not by the issue — and it is the one that would have kept biting: it is a silent loss of the similarity property, scale-dependent rather than sparsity-dependent.
A THIRD site carried the same unsafe division — cascade/matrix_cascades.py qr(), found by grepping the tree for divisions by a _fhypot/_modulus result rather than by a failing test. Only complex input reaches that loop (real input dispatches to the native srmech_qr_f64), and on the measured tiny x0 column it left a residual tail of 1.6e-1 with |α| = 0.7205 against ‖x‖ = 0.7385 — i.e. a non-unitary Q. Fixed identically, and pinned by a new QᴴQ == I / Q·R == A property over five input magnitudes. The audit found no fourth site: every other _fhypot/_modulus_c use in the package is a comparison or a magnitude readout, where snapping a sub-1e-17 value to zero is benign (it makes the deflation test deflate, which is the desired behaviour) — division is the only unsafe consumption, and all three instances of it are now scaled.
_fhypot itself is left alone: it is the float projection of the Class-N rational.hypot cascade, its behaviour is shared with the C peer, and repairing its small-argument accuracy is a wider change than a correctness rc should carry. Filed as a latent issue: any future x / _fhypot(x) is a defect by construction.
-
Hardening (not a third wrong-answer defect) — the QR step now runs on the ACTIVE block. Negligible subdiagonals are pinned to exact zero and the shifted step is applied to
H[lo:m, lo:m], the trailing unreduced block; a pinned zero splits the matrix, so the spectrum is the union of the blocks' spectra and the off-diagonal blocks need no update. This is the textbook Francis structure (G&VL §7.5.2) that the previous code omitted. Measured honestly: over the ratchet's 230 (graph × relabelling) cases the split fires in 181, and forcinglo = 0with everything else at rc285 leaves 229 of 230 still correct, the one straggler drifting to 6.5e-9 against 3.9e-14 with the split respected. So it buys real accuracy — but it is not what produced the wrong star spectrum, and it is not what produced the 1.4e-2 broom drift. Listed separately from the two genuine defects rather than padded into them. -
#1440's "second defect" (phase-blindness) is NOT real — corrected. The report observed byte-identical output for a charge on edge 0 / 1 / 2 / none of a star and concluded
mat_eigvalsdiscards the imaginary part. It does not. A star is a tree: it has no independent cycle, so every charge on it is gauge-equivalent to zero and the spectrum is genuinely phase-invariant — and so is every principal submatrix's, since a principal block ofD·L·DᴴisD_k·L_k·D_kᴴ. The identical outputs were a correct invariance reported through a then-wrong solver. Verified directly:mat_eigvalsreturns±1for Pauli-Y (whose real part is the zero matrix) and±ifor a rotation, and on a gauge-nontrivial charged triangle it agrees withhermitian_eigendecomposeto 12 digits. No raise was added and no dtype-honesty repair was needed:mat_eigvalsis the general non-Hermitian solver and complex input is its purpose. Both facts — phase-sensitivity on a cycle, phase-invariance on a tree — are now pinned as tests so the failure class cannot silently appear later. -
tests/test_laplacian_kernel_invariant_rc285.py— the ratchet, and the deliverable that outlives the fix. For everydense_laplacianoutput,min(eigvals) == 0to tolerance, asserted over all six shipped eigensolvers (mat_eigvals,jacobi_eigvals,hermitian_eigendecompose,symmetric_eigendecompose,mat_hermitian_eigendecompose, and the publicmatrix_cascades.eigvalsthat delegates tomat_eigvals), across 6 graph families × 8 sizes, weighted and disconnected variants, plus stars explicitly. Also: cross-solver agreement; vertex-relabelling invariance over 5 permutations (the general form of defect 1, and the property that caught defects 2 and 3); closed-form star / complete / cycle spectra; the reflector's definingP·x = α·e₁with|α| = ‖x‖at seven input scales includingx0 = 6.9e-17; Hermitian input reducing to Hermitian tridiagonal form; and a coverage ratchet that fails if a new eigensolver is exported without being added — #1440 existed because one solver of six sat outside the test matrix.
Proof of redness is in-suite and permanent. _mat_eigvals_pre_rc285 is a faithful copy of the shipped algorithm minus the Hessenberg step; it is asserted to reproduce 2∓√3 on K(1,3) and the wrong λ_min ≈ 1 on the relabelled path, and to agree with the shipped code where the bug does not bite. A ratchet never shown to go red is not a ratchet.
-
ADR-0009 parity — the compiled projection does not share the defect, because it does not have the capability. Verified against a
.sofreshly built from this worktree's C source (721 exportedsrmech_*symbols): the C tree hassrmech_jacobi_eigvals(real symmetric),srmech_hermitian_eigendecompose_ws(complex Hermitian) andsrmech_eigvec_exact, and no general non-Hermitian eigensolver and no Hessenberg reduction at all.mat_eigvals's balancing, reduction, deflation loop, Wilkinson shift ladder and{QR}are Python-only; only theRQproduct routes to C viamat_matmul. Both projections were checked on stars K(1,3)…K(1,16) and agree (λ_min ≈ 0 for every solver on both paths). This is an open ADR-0009 §1.3-mechanism-2 gap, filed here rather than fixed:mat_eigvalsis classifiedcomposition_of_c— a bucket whose annotation reads "standalone-ready" — while a bare-C host cannot run it. No C change was made and ABI stays 6; closing the gap means implementing the complex Hessenberg-QR eigensolver in C, which is its own rc. -
No public-callable surface change. Signature, return type and raising behavior of
mat_eigvalsare unchanged; the three new helpers (_householder_reflector,_hessenberg_complex,_cmax_component) are private. Notool_schema/_tool_docs/ registry / rosetta /test_mcpripple applies. -
Blast radius, verified rather than assumed. The issue reported 2
mat_eigvalscall-sites; there are 4 in-package (cascade/matrix_cascades.py:616,qm/pseudo_hermitian.py:249and:303,signal_processing/closed_form_ops/esprit.py:88) — and the first is the public opmatrix_cascades.eigvals, which inherited the wrong spectrum verbatim and is a fifth reachable surface the issue did not count. Tree-wide symbol occurrences:mat_eigvals104,jacobi_eigvals205,hermitian_eigendecompose192,symmetric_eigendecompose136,fiedler_vector45. No committed research result consumesmat_eigvalsormatrix_cascades.eigvals, so the "no lodged result is contaminated" conclusion holds — but it holds over a wider surface than stated.
[0.9.0rc284]¶
§100 G1 — laplacian.recursive_cut earns its C entry point: the out-of-core recursive spectral bisection driver now runs standalone in C. The deepest ADR-0003 parity gap, and the one G2 (genome_from_graph) and G3 (the GRAPH genome_partition) both dead-ended on.
-
What was actually missing was the RECURSION, not the mathematics. The premise this rc started from — that closing G1 needs a Fiedler-vector computation built in C with no external math library — turned out to be already solved.
srmech_laplacian_fiedler_sparse_filehas been native since rc168, streams its adjacency through the PAL, carries no node cap, and already had the §101 tick threaded through it at phasePARTITIONING;srmech_rational_sqrt(via the file-locallap_sqrt) has supplied the only square root it needs since rc45, nolibm. What had no C entry was thewhile pendingloop AROUND the engine: the disk-backed work queue, the induced-subgraph relabel, the sign-split, and the tome lifecycle. So a bare-C host could bisect once and no further — which is to say it could not build a partition at all.srmech_laplacian_recursive_cutis that loop. -
New C symbols
srmech_laplacian_recursive_cut+srmech_laplacian_recursive_cut_arena_bytes(ABI stays 6). Additive, and they reuse the existingsrmech_progress_tick_cb_ttypedef rather than introducing one, so the callback-typedef rule that drove v2→v6 does not fire. Iterative despite the name (JPL Rule 1): an explicit arena-backed LIFO carries(serial, depth)in the Python driver's exact pop/append order.wsis a caller arena sized by the companion_arena_bytes— note it is in BYTES, unlike thefiedler_sparsefamily's DOUBLES, an inconsistency that already existed in this header betweenfiedler_sparse_fileandk_extreme_modes_file. No file-scope statics, so it is reentrant by construction; nothing on the path calls_catalog_dataorsrmech_genome_obtain_manifest. -
The
orig_to_localdict dissolved into a binary search. The Python driver builds a per-bisection relabel dict; a no-malloc C peer cannot. It does not need to: node sets are sorted ascending by construction — the root is0..n-1and every child preserves the parent's relative order through the stable sign-split — so the relabel is a binary search over the set itself. No map, no hash, no auxiliary allocation. The invariant was always there, hidden inside the dict. -
Three new PAL primitives —
srmech_plat_mkdir/srmech_plat_file_remove/srmech_plat_file_replace. The real blocker under G1, and the reason it could not have been closed by writing spectral code alone: the PAL had file read/write/size/stat and directory iteration, but nothing that could create a scratch directory, drop a consumed work file, or move a finished one into place.remove()/rename()are C89 stdio;mkdirand replacing rename are not (POSIXmkdir(path,mode)vs_mkdir; POSIXrename()replaces an existing destination atomically while Win32's fails on one, soMoveFileEx(…, MOVEFILE_REPLACE_EXISTING)is the Win peer). That split lives in the PAL;srmech_laplacian.cstays#ifdef-free.mkdirandremoveare idempotent (already-exists / already-gone are success), matchingos.makedirs(exist_ok=True)and a pre-checkedos.remove. -
Byte-parity is measured, not asserted — 0 mismatches across 83 cases.
tests/test_recursive_cut_parity_rc284.pyruns the native driver against the forced-pure driver over 14 graph shapes × 4max_tomevalues, comparing tome file bytes, tome contents, tome ordering, tome file names, sizes and status. The matrix deliberately covers where an out-of-core queue driver actually breaks: ring-of-cliques with real community structure (3×3, 4×4, 5×6), path graphs including an odd 31, degenerate spectra (complete graphs K8/K16), disconnected graphs (3×4, 5×2), edgeless, single-node, two isolated nodes, andn == 0. Plus the depth guard atmax_depth0–3 and the §101 cancel at three cut points.tests/test_srmech_recursive_cut.cadds 22 C-side smoke checks. -
⚠️ A real divergence the parity matrix caught, worth recording. The first C draft early-returned "no tomes" for
n == 0. The Python projection does not: it seeds the queue with the empty root set and retires it as one empty tome. That is the shipped public contract, so the C was corrected to match it rather than the reverse — per ADR-0009, the capability is the invariant, and where the two disagree the incumbent contract wins unless there is a reason to move it. An early-out would have been a silent behaviour break for any caller indexingtome_paths. -
CEIL_WIRE_GLUE_GAPS11 → 10. G1 leaves_KNOWN_GLUE_GAPSand joins_WHOLE_OP_C_PEER, machine-checked both ways: the symbol is declared inc/include/srmech.hand genuinely reachable through the op's dispatch glue (the rc273 defect shape — a real C symbol declared and never dispatched — is what the second check exists to reject). -
⚠️ Scope finding: this closes ONE gap, not three. G2 and G3 are unblocked, not closed. Both dead-ended on G1, so the dependency claim was right; the inference that all three therefore fall together was not. Each still needs C surfaces that
recursive_cutdoes not supply — G3 needs exact-integer participation, the antimode histogram (bins / threshold / peaks / valley / gap / bimodality) and per-node classify; G2 needs all of G3 plus its own in-RAM_induced_subgraphrelabel, the per-groupgraph_to_kernel→mint_strandloop, and strand assembly. They are separate rcs. The ceiling therefore lands at 10, not 8, and saying so is the honest outcome rather than declaring a surface closed that is not. -
The op's rosetta bucket stays
composition_of_c— it still validates, and writes the packed graph, in Python before dispatching the recursion. This followsgenome.recall, which likewise holds a whole-op C peer without moving bucket. No compute ornon_computebucket counts move, so the count-tests and the rc170/rc177/rc183 pins are untouched.recursive_cut's public signature is unchanged, so notool_schema/_tool_docs/ registry / rosetta /test_mcpripple applies.
[0.9.0rc283]¶
The CI Python suite runs in parallel: pytest-xdist at -n auto --dist loadfile, and the two order-dependence bugs that were hiding behind serial execution. No coverage changes — every test, every assertion, every fuzz iteration survives; only their placement across processes changes. Measured on one quiet 8-core WSL2 box, native ext4, same tree, same .so, back-to-back.
Measured on CI itself (the authoritative numbers — dedicated runners, no contention):
| cell | before | after | |
|---|---|---|---|
| ubuntu-latest py3.10 | ~26 min | 13 m 33 s | ← wall-clock driver |
| ubuntu-latest py3.12 | ~24 min | 11 m 17 s | |
| windows-latest py3.12 | ~18 min | 11 m 48 s | |
| macos-14 py3.12 | ~13.9 min | 5 m 17 s | fastest cell; 3 vCPU → -n auto = 3 |
The CI wall clock goes ~26 min → ~13.5 min, roughly 12–13 minutes back per run. macOS gains most in relative terms (2.6×), which is the opposite of what a core-count argument alone would predict and is worth noting for anyone tuning this later.
Corroborating local measurement (one 8-core WSL2 box, native ext4, same tree and same .so on both sides — so the ratio is apples-to-apples even though the absolute times are not CI's):
| run | wall | pytest-reported | result |
|---|---|---|---|
| serial | 2529.0 s (42:09) | 2327.5 s | 9604 passed, 30 skipped |
-n 4 --dist loadfile ×3 |
1154.7 / 1004.9 / 943.4 s | 1059.5 / 919.8 / 862.2 s | all three green |
-n 8 --dist loadfile |
924.4 s (15:24) | 857.2 s | 9604 passed, 30 skipped |
-
The spread across those three local runs is contention, not variance in the change. A second agent session was running its own pytest on that box during the earlier runs (load average ~3.7); as it drained, the identical command went 1154.7 → 1004.9 → 943.4 s. The investigation note's 2.92× (2618 → 897 s) is broadly corroborated at the top of that range, and its serial half reproduces closely (2529 s vs 2618 s).
-
These local figures predate the rc282 rebase and are kept as-is, deliberately. Re-verification after rebasing onto rc282 (whose genome read-I/O work also cuts suite time) gives
-n 4at 784.8 / 756.2 / 766.4 s across three more consecutive runs on a now-quiet box, 9610 passed / 30 skipped / 0 failed each. That is not restated as a ratio here, because the only serial baseline on hand is the pre-rc282 one and dividing across two different trees would be a fabricated number rather than a measurement. -
Scaling past 4 workers is real but sub-linear: 8 workers returned 2.74×, not 4×. Under
--dist loadfileno run can finish faster than its slowest single FILE, andtest_qm_so8_triality_c_rc146.pyis ~453 s of forced-pure 28×28 triality arithmetic in six tests. That floor starts to bind around 6 workers, which is why the extra four workers bought only ~22 %. Splitting that file is the prerequisite for scaling further; it is deliberately NOT done here, because splitting it means touching what it asserts and this rc changes no coverage. -
-n auto, deliberately, NOT a fixed-n 4. xdist resolvesautothroughos.cpu_count()here — psutil is intentionally not a dev dependency — which is exactly each GitHub-hosted standard runner's vCPU allotment: 4 onubuntu-latestandwindows-latest, 3 onmacos-14. A hard-coded-n 4would oversubscribe the 3-core macOS cell and would silently become wrong if GitHub re-sizes runners.--dist loadfile(whole file → one worker) keeps each file's tests in authored order and confines the process-globalos.chdir/os.environmutations a few files perform. -
Blocker 1 — a
parametrizethat minted its own test ID at COLLECTION time.test_bus_cipher_transport_c_rc179.pyhados.urandom(2000)evaluated inside the decorator, so every worker process generated a different 2000-byte payload and therefore a different test ID; xdist requires all workers to collect an identical list and aborted the run before executing anything. The payload is now minted inside the test body behind apytest.param(None, id="urandom2000")sentinel. Coverage is unchanged and the fuzzing is strictly better: it re-randomises every RUN instead of once per collection. It also fixes a latent reproducibility hole — a failure under the old form could not be re-run, because the payload that triggered it was already gone. -
Blocker 2 —
clear_registry()leaked a wiped registry into every later test in the process. This is a REAL order-dependence bug, and the serial suite was passing on alphabetical luck.srmech.signal_processing.path_registry.clear_registry()is documented as a "test-isolation utility", but it is a destructive PROCESS-GLOBAL wipe with no restore, andtest_signal_processing_scaffolding.pycalls it ~10× including infinally:blocks — so it finishes with the registry EMPTY. The eager registrations it destroys (rfft/fft/ifft/pi_cascade/hdc_truncation, registered by importingpath_b_ops, plus therbs_hdc_*ops) never come back: those modules are already insys.modules, so nothing re-imports and nothing re-registers, and_ensure_loaded()cannot help because it short-circuits on any op already present in_REGISTRYand becauserffthas no lazy loader at all. Serial runs survived purely because..._rfft.pysorts BEFORE..._scaffolding.py; the dependency is on test ORDER, not on xdist, andpytest tests/test_signal_processing_scaffolding.py tests/test_signal_processing_rfft.pyreproduces it serially in under a second.tests/conftest.pynow snapshots and restores the registry around every test, so no test can leak a wipe — or a stray registration — into any other. The suite is now genuinely order-independent rather than accidentally-ordered. -
Blocker 3 — the bus registry directory is a PROCESS-EXTERNAL singleton, and two workers sweeping it deleted each other's sockets. This one only appears on a REPEAT run, which is the argument for repeating.
-n 4run 1 was fully green; run 2 failedtest_bus_aio.py::test_aio_connect_refusal_window_is_bounded(FileNotFoundError: no bus endpoint) and::test_async_encrypted_channel_seed_propagates(BusError: reader exited; peer closed). Endpoint names are uuid-unique, so this is not a name collision — the shared thing is the DIRECTORY.srmech.busregisters every endpoint under~/.srmech/, a fixed HOME-based path visible to every process on the machine, andlist_endpoints(cleanup_dead=True)scans that directory and deletes any endpoint it judges dead. A socket that is bound but not yet accepting — exactly what the refusal-window test constructs on purpose — is indistinguishable from a dead one, so one worker's discovery sweep unlinks another worker's in-flight socket. Not an ordering bug and not a product defect: the sweep is behaving as designed, and a single-process run simply never has a second sweeper. Fixed by removing the singleton rather than serialising around it —tests/conftest.pynow pointsHOME(andUSERPROFILE, for the Windows cell, wherePath.home()reads that instead) at a private short-lived temp dir for the two modules that create real on-disk endpoints. Preferred over anxdist_grouppin, which would have forced those files onto one worker and given up their parallelism to work around a resource we can simply un-share. The temp dir is a shortmkdtemp, nottmp_path_factory, whose nested directory names overflow theAF_UNIXpath cap on their own. -
The
sun_pathcap is PLATFORM-SPECIFIC, and treating it as one number cost a macOS CI failure — so it is now computed and asserted rather than assumed. The first cut of the fixture above carried a comment reading "capped at ~108 bytes". That is Linux's number; macOS and the BSDs capsockaddr_un.sun_pathat 104. macOS is worse on both terms, becausemkdtemphonoursTMPDIRand GitHub's macos-14 runners set it to the per-user/var/folders/<xx>/<~30 chars>/T/form (48 chars, confirmed from the failing job's own log) rather than Linux's 4-char/tmp. The arithmetic lands exactly on the boundary: 48 +mkdtemp(12) +/.srmech/bus-aio-test-<12hex>-sink.sock(44) = 104 against a 104-byte buffer whose last byte is the NUL — over by one. Ubuntu and Windows passed;macos-14 • py3.12failed one test (test_bus_aio.py::test_async_pipe_forwards_broadcasts) with a bareOSError: AF_UNIX path too long, 9620 passed alongside it. _sun_path_cap()returns 108 on Linux and 104 elsewhere, defaulting any unrecognised platform to the tighter value — an over-strict cap costs a readable error, an over-loose one costs an opaqueOSErrorminutes into a run._bus_home_base()prefers the platform default temp dir (soTMPDIRis honoured wherever it fits — on Linuxgettempdir()is already/tmp, so nothing changes there) and falls back to/tmponly when the default would overflow./tmpis safe here rather than merely short: POSIX requires it to exist and be writable,mkdtempcreates our directory mode 0700 so privacy does not depend on the parent, and on macOS — where/tmpsymlinks to/private/tmp— the kernel length-checks the bytes actually passed tobind(), so the 4-char form is what counts (and even the resolved 12-char path would still fit).- The fixture now asserts its own worst case, computing the longest endpoint path it could produce and failing immediately with the cap, the measured length, the offending path, and the base directory. The worst case is budgeted at a 48-char endpoint name against today's actual 26 (
aio-test-+ 12 hex +-sink), so a longer name added later cannot silently re-introduce the overflow. Worth recording, since it was asked: the async module is the worst case because of itsaio-name prefix, not a longer suffix —-src/-sinkare identical in both bus suites, making the async names 26 chars against the sync suite's 22. -
Verified by simulating the failure rather than reasoning about it: with a
TMPDIRof exactly macOS's 48-char length, the old code produces exactly 104 bytes (overflow) and the new code falls back to a projected 82; both bus modules run 141 passed under that longTMPDIRand under the default. -
A fourth leak, found by audit rather than by a failure:
test_env_var_catalog_pathnever restoredSRMECH_CASCADE_PATH. It assignedos.environ[...]directly, pointing the cascade-catalog loader at atmp_paththat pytest deletes at teardown, and left it there for every subsequent test in the process. It had not yet caused a failure — under serial ordering nothing downstream reloaded the catalog — but under xdist the file→worker assignment decides what runs next, so the leak is reachable and would present as an intermittent flake. Converted tomonkeypatch.setenv(restored automatically) with the catalog cache cleared on the way out as well as in. -
Ratchet semantics verified intact under
-n, not assumed. A ratchet that silently stops counting in a worker would be a false green, which is the one failure mode parallelism could plausibly introduce. It does not occur here, and the reason is structural: every down-only ratchet — JPL audit,CEIL_NON_COMPUTE_OWEDand thenon_computefour-way split, rosetta completeness / transitive,CEIL_WIRE_GLUE_GAPS, the carrier ceilings,tools.total— derives its count either from files on disk (c/sources,rosetta_classification.ndjson) or fromconftest.rosetta_live_objects(), which performs its ownpkgutil.walk_packagesimport over every root. None of them counts state accumulated by other test files' imports, so a worker's count is complete no matter which subset of the suite it drew. Confirmed by running the ratchet files both under-n 4and in isolated single-worker processes and comparing the counts. -
Stability was established by REPEATING, not by one green run — and repeating is what caught Blocker 3. The first
-n 4run after the first two fixes was fully green; the second failed. Validation is three consecutive full-n 4runs, all 9604 passed / 30 skipped / 0 failed (and three more after the rc282 rebase, 9610 passed / 30 skipped / 0 failed), plus a targeted 10× stress of the two racing bus modules pinned onto separate workers (-n 2 --dist loadfile), 0/10 with failures — against a deterministic 1-failure rate before theAF_UNIXpath fix and an intermittent rate before the HOME redirect. Even so, thesun_pathoverflow still reached CI, because every local run was on Linux: repetition catches order-dependence, not platform-dependence, and the two need different instruments. That is what the fixture's explicit cap assertion is for. -
test_genome_census_rc267behaves identically under-n— 10 passed in 0.96 s serial, 10 passed in 3.02 s at-n 4, no hang and no core dump in either mode, on a.sorebuilt from this tree's own C source. It is excluded from the local runs above only because its status was disputed going in; nothing here reproduces the reported 240 s / crash, which is consistent with the stale-native-library explanation rather than a defect in the file. -
No production
srmech/source changed (onlyversion.py's bump). Every fix is intests/, CI config, and packaging.pytest-xdist>=3.5joins thetestsanddevextras in bothpyproject.tomlandpyproject-pure.toml. The publish workflow's cibuildwheel smoke (test_native_sha256.py, one file) stays serial — parallelising a single file buys nothing and cibuildwheel containers are core-constrained.
[0.9.0rc282]¶
§102 / F1253 — the genome READ path pays its own bill: a held handle, a streamed catalog, and a ratchet so the constant cannot creep back. rc280 made section_counts read only the node_ids prefix of each section. It fixed the asymptotics and left a syscall constant — and at field-store scale the constant was the cost. This rc is a performance repair, so every claim below is measured; the generating code is committed (notes/rc282_genome_read_io_probe.py, notes/rc282_c_open_count_probe.sh).
-
⚠️ The fast path was the slow path.
genome._read_region_prefixopenedturns.binon every call, and_section_node_idscalls it in a growth loop — so a scan paid a measured exactly 2.0 opens per section (401 opens at P=200; ~481,762 extrapolated to the 240,881-section field store) while advertising itself as the targeted read. Both now page through an already-open handle threaded from the caller:_read_region_prefix/_read_region/_region_leaves/_section_node_idstake an optionalf=, andplasmid.section_counts(plus the twogenome_integrate_plasmidsper-section loops) hold ONE handle for the whole pass. Every one of them keeps an open-once-here convenience path, so no caller breaks; even a lone_section_node_idsnow costs 1 open rather than 2, because the growth loop runs inside one handle. Measured: 401 → 2 opens at P=200, and flat across a 25/50/100/200 sweep (was2P + 1). -
The whole-body slurp on the head-only catalog read is gone.
_catalog_data's v12+ HEAD-ONLY branch — the branch every store written today takes — didturns.bin.read_bytes(), materialising the entire body just to derive a catalog that is one forward pass. It now derives from a streamed scan (_stream_body_blocks+_scan_body_stream), folding each region's digest the moment the next region opens, so RAM is bounded by the largest single REGION instead of by the file — the same bound_read_region/genome_windowalready advertise. Measured: 1 → 0 whole-body slurps per catalog derivation. To keep this provably an I/O-only change, the whole-body and streaming paths now share one state machine (_ScanState) and one assembly point (_build_manifest_data_from_hexes); a test asserts the two derive byte-identical catalogs, region digests andbody_sha256chain included. Honest scope: this bounds RAM, not BYTES READ. Deriving a catalog still scans the whole body — that is ADR-0003 (the chromosome array is a plaintext TOC and is never stored), and removing it would need a format change, which this rc does not make. -
ADR-0009 parity: the compiled projection had the same defect, and it is fixed too.
sc_refillpaged its 64 KiB window throughsrmech_plat_file_read_region, whichfopen/fcloses per call → measured 28 / 53 / 103 / 203 opens over the same 25/50/100/200 sweep (≈ 1 open per section, linear in P — the same shape as the scripting projection at a different constant). New internal PAL triosrmech_plat_file_open_ro/srmech_plat_file_read_at/srmech_plat_file_close_ro(caller-owned handle, no malloc, JPL-clean) letssrmech_genome_section_countshold one handle for the whole scan: measured 4 / 4 / 4 / 4 — constant in P. Additive internal symbols, no public-callable surface change, ABI stays 6,GENOME_FORMAT_VERSIONstays 15. -
Three false comments corrected — and this is why the rc ships a test, not a comment.
genome.pycarried# the manifest read — never opens turns.binon a line whose catalog read does exactly that on every store written today;genome_catalog's public docstring made the same claim ("it NEVER opensturns.bin"), true only of the v≤11 full manifest it long outlived; andgene_express_plan's "bounded I/O (bytes-touched ≪ full body)" was scoped to the plan's per-region reads but read as covering the call, whose catalog derivation scans the body. All three are corrected in place with the actual cost stated. A comment claiming a performance property is not a test — which is plausibly how the slurp survived review — so the properties are now pinned mechanically. -
tests/test_genome_read_io_ratchet_rc282.py— a down-only ratchet.CEIL_BODY_OPENS_PER_SCAN = 2andCEIL_BODY_SLURPS_PER_CATALOG = 0, asserted over a 4-point sweep spanning 8× in P, in the style of the JPL / Rosetta / wire-glue ratchets: these ceilings may only ever be lowered. The instrumentation wrapsbuiltins.open,Path.openandPath.read_bytes, because the body is reachable through all three seams and hooking one would let a regression through the others. Asserted on syscall counts, not wall-clock: the count is exact and portable, the seconds are not (see below).
Correction (rc296). This ratchet pinned one projection, not the capability. Every one of its six tests took a
pure_onlyfixture that monkeypatched native dispatch OFF, soCEIL_BODY_OPENS_PER_SCANconstrained the scripting projection only — and the compiled-side result this rc reports two bullets above ("measured 4 / 4 / 4 / 4 — constant in P") shipped with zero test coverage. The compiled read path could have regressed to per-call re-open — precisely the rc280 defect this rc exists to fix — with the file still green. Per ADR-0009 that is a parity gap inside a ratchet: a down-only ceiling that can only ever run one projection cannot enforce an invariant defined across both. The number itself was right. rc296 re-measured it with two independent instruments and it reproduces exactly: 4 opens ofturns.binper native scan, flat across the same 25/50/100/200 sweep — 1 Python-side (the pre-dispatch_section_entriescatalog derivation) + 3 C-side, with 5 read-path opens made by the C library in total per scan (2manifest.json+ 3turns.bin). Two further findings: the fixture was inert on four of the six tests —_catalog_data/_section_node_ids/_read_region/_read_region_prefixcontain no native dispatch at all (verified by AST), so a blanket application looked like a per-test scoping decision that had not been made; and the generating code this rc committed for the compiled-side claim,notes/rc282_c_open_count_probe.sh, had not run since rc290 — thethe_one→couplingrename left it raisingTypeError. Provenance that does not execute is not provenance. rc296 repairs the probe, applies the fixture only where it changes what runs, and adds the seam that makes the compiled projection measurable from the suite at all.
- Measured before/after (
notes/rc282_genome_read_io.ndjson; fixture matched to the#876cost probe, 5 timing repeats per point, pure path):
| P | opens before | opens after | scan before | scan after | targeted ÷ full-sequential-decode |
|---|---|---|---|---|---|
| 25 | 51 (2.04/section) | 2 | 0.0230 s | 0.0203 s | 0.80 → 0.99 |
| 50 | 101 (2.02/section) | 2 | 0.0544 s | 0.0330 s | 1.31 → 0.76 |
| 100 | 201 (2.01/section) | 2 | 0.0994 s | 0.0699 s | 1.20 → 0.72 |
| 200 | 401 (2.005/section) | 2 | 0.1715 s | 0.1517 s | 1.06 → 0.92 |
Whole-body slurps per catalog derivation: 1 → 0 at every point. The ratio column is the #876 claim's own measure — the rc280 targeted read sitting above 1.0 means it was genuinely slower than reading and decoding the entire body, which is reproduced here at mid-sweep; after the fix it is below 1.0 throughout.
-
Measurement honesty — the prior-art magnitude did not reproduce. The
#876probe reported 1.8× slower-than-full-decode at P=200; this host measures 1.06× there (and 1.2–1.3× at P=50–100). The 2.0 opens/section is exact and reproduced identically; the wall-clock multiple it buys is platform-dependent, being dominated by how expensiveopenis on the filesystem under test. Single-shot runs on this host showed noise of the same order as the effect, which is why the numbers above are 5-repeat averages and why the ratchet asserts syscall counts, not seconds. The defect and the fix are real on both hosts; the speedup multiple is not a portable constant and is not claimed as one. -
Five more false greens, surfaced by the seam repair — and the honest statement they force. Routing every body read through the one seam made the catalog scan visible to the §134/§98 demand-load tests, which had been summing
_open_body_robytes and asserting exact totals (assert 4924 == 256). Behavior did not change; the observation became honest. Those tests were bounding the gate loop while reading as though they bounded the whole call. The fix is not to narrow the instrumentation back — that restores the false green — but to separate the two terms the sum conflated, which map exactly onto the code: a plan term (the per-region gate reads — the guarantee the chromatin work actually makes, still asserted at full exact-byte strength) and a catalog term (the whole-body scan, a fixed cost independent ofcell_state, asserted separately as a known cost). New sharedconftest.BodyReadProbetags sessions causally — it wraps_catalog_dataas well as_open_body_ro, so a handle opened inside a catalog derivation is labelled as such and reordering cannot mislabel it. Updated:test_express_plan_rc135.py,test_express_plan_chromatin_rc269.py(×3),test_cellstate_chromatin_rc274.py.
The property that turned out not to be true, stated plainly: gene_express_plan's call-level I/O is not bounded the way the module docstrings advertised (bytes-touched ≪ full-body-bytes). It cannot be, while the chromosome table is derived by scanning — ADR-0003 forbids storing it. Call-level bytes-read is ≥ the whole body; only the per-region gate reads are bounded. The docstrings now say so. Closing that gap needs the format change this rc explicitly does not make.
-
Probe liveness — the seam lesson applied to the tests themselves. An upper bound alone passes trivially at zero, so a disconnected probe would go green.
BodyReadProbe.assert_live()asserts the probe actually FIRED (non-zero sessions and non-zero bytes, for both terms) and is called in every bounded-I/O test. Verified by falsification: re-introducing the pre-rc282read_bytesslurp so the catalog bypasses the seam makes the probe go red, not silently green. -
_open_body_ronow opens throughPath.open, notbuiltins.open. It documents itself as the seam a caller measures bytes-touched through, but abuiltins.openthere is invisible to thePath.openhooks this package's own suite uses to prove region-bounded I/O — a seam that silently stops observing is worse than no seam, because the assertions keep passing. Every other body read in the module already usedPath.open; this one now agrees. (Caught by two existing persistence tests going red the moment the catalog read moved onto the seam.)
[0.9.0rc281]¶
§135 / F1251 / G6 — the amplify + copy_number_of C peers, and a ratchet so this whole gap-class cannot recur. Two halves: a parity correction, and the systemic fix that would have caught it.
-
⚠️ "Transparent to readers" is not "C-host parity" — the distinction this rc exists to draw. rc273 shipped
amplify/copy_number_ofPython-only, and its C test proved a true thing: the copy-number field is byte-transparent to every existing C reader (srmech_genome_gene_expressreturns on the0x47marker before reading any field), so an amplified genome keeps reading correctly with no C change. From that it concluded "no C change needed". That conclusion conflates a C reader is not broken by the field with a C host can use the field. There was no C path to WRITE the count or to READ its value — a bare-C host could only ever ignore the copy-number axis. The C-host parity audit named this its cleanest exhibit of a 1:1-parity gap wearing an honest-looking label (docs/srmech/notes/c_host_parity_audit_rc273.md§2 G6). -
New C symbols
srmech_genome_amplify+srmech_genome_copy_number(ABI stays 6;GENOME_FORMAT_VERSIONstays 15). A bare-C host now performs both halves end-to-end: find the FIRST plain0x47gene cap by inline label, and either rewrite it to carry an exactuint64big-endian count in what was the cap's NUL padding (right after the label's NUL — the §127 active-count / §129 regulatory-mask placement) or read that count back. Both are additive plain symbols with no new callback typedef, so the ABI does not move; and rc273's field is already part of the on-disk format, so the format version does not move either.n == 1writes the plain cap — byte-identical to a never-amplified gene, no wire spent — so onlyn >= 2spends the 8 bytes; a stored 0 (plain padding, or any pre-rc273 genome) reads back as 1, which is what makes the field back-compatible in both directions. Both Python ops now dispatch the whole op to their peer. JPL-clean (≤60-line functions, ≥2 asserts, no goto/malloc/recursion); Class-I/N exact integers, no float, neverabs()— a multiplicity has no sign to strip, so then >= 1guard is a domain gate, not a Class-K pin-slot site. Both rows movecomposition_of_c→c_dispatched. -
Byte-parity is proven, not asserted.
tests/test_genome_amplify_c_rc281.pyruns the native path against the forced-pure path across labels × counts (1, 2, 3, 255, 256, 65535, 2²⁰, 2⁴⁰, 2⁶⁴−1) × leaf_dims (32/64/128): 0 mismatches, on the carriersectorsas well as the bytes. Also pinned:n == 1is byte-identical to a plain gene through the native path too; exactly ONE block changes per amplify; the input strand is never mutated; the error contract is unchanged when the peer declines; and the rc273 transparency property still holds with the peer in the loop (genes/recallsee an amplified cap as the same always-expressed plain gene). -
The systemic fix —
test_rosetta_transitive_standalone.pynow ratchets WIRE-FORMAT GLUE. The audit's root-cause finding was that the existing ratchet treats everycomposition_of_cop as a leaf: it proves a composite reaches C-backed leaves but never that a bare-C host can run the composite's own glue. That is weaker than ADR-0003 §2/§3 ("every composite, including orchestrators, gets its own C entry point"), and it is exactly howintegrate,mint_strand,amplifyandcopy_number_ofall slipped through. The new ratchet scopes to the wire-format surface — the ops that lay out srmech's own on-disk byte structures (srmech.amsc.genome,srmech.amsc.plasmid, plus the out-of-corelaplacian.recursive_cut) — where "a bare-C host cannot run this" is load-bearing rather than theoretical, and where the standing genome-must-exist-fully-in-C directive applies. Pure-mathcomposition_of_cops stay deliberately out of scope; the existing transitive walk already covers them. -
What counts as C-reachable is verified, not declared. An in-scope op must either name a whole-op C peer in
_WHOLE_OP_C_PEER— machine-checked twice over: the symbol must be declared inc/include/srmech.h, and must actually be reachable through the op's dispatch glue — or sit on the documented_KNOWN_GLUE_GAPSallowlist. Deliberately not sufficient: a private helper that dispatches a C primitive.condensereachingsrmech_genome_chromatinfor its cap bytes is "reaches a C leaf" — the weaker property this ratchet exists to reject — because the range-find, region resolution and splice around that cap remain Python-only. -
The allowlist is DOWN-ONLY and was derived from the tree, not hand-written.
CEIL_WIRE_GLUE_GAPS = 11, pinned by a test that fails if it grows, mirroring the JPL Rule-5 exempt list andCEIL_NUMPY_CARRIER. Cross-checking the live rosetta ledger against the live C surface showed the audit's snapshot had already moved: rc276 closed G4 (integrate) and rc277 closed G5 (mint_strand), and this rc closes G6, so the honest starting list is 11, not the audit's larger set —active_telomere,condense,decondense,genes,genome_genes,genome_genes_expressed,genome_from_graph(G2),genome_partition(G3),mint_plan,recursive_cut(G1, the deepest —genome_partitionandgenome_from_graphboth dead-end here), andplasmid.add_plasmid. Each entry carries its reason and audit reference; further tests fail if an entry goes stale, silently closes, or is duplicated into the peer map. -
The ratchet is proven to bite. Three positive tests drive it with synthetic regressions rather than trusting it: a new un-C-reachable genome op on neither list is flagged (and a pure-math op is correctly left alone — no scope creep); a peer declared with an invented C symbol is caught by the header check; and a peer declared but never dispatched is caught by the reachability check — the last being precisely the rc273 error shape, a real C surface nearby with no path from the op to it.
-
Ledger.
composition_of_c229 → 227,c_dispatched251 → 253. Thenon_computepins do not move (total 200;composes_c128 /host_glue21 /dev_tooling51) — both ops moved between two compute buckets, so the non_compute ledger is untouched andowedstays 0.tools.totalstays 456: no new public callable ships, the two existing ops merely earned C peers. Both C registries regenerated.
[0.9.0rc280]¶
§102 / F1253 — section_counts reads only what it needs: the ~22-hour re-derivation is gone. rc278 shipped section_counts as the genome-native SSoT re-derivation of {global_id: n_sections}; rc279 correctly made it opt-in behind the free streamed accumulator after a downstream session measured it over the FULL simplewiki store — 240,881 sections at ~0.33 s/section ≈ 22 HOURS. Routing around it was right, but it left the fallback unusable for its two real jobs: RESUMING against a store you did not just build, and VERIFYING the accumulator against the on-disk SSoT. rc280 fixes the fallback itself. The counts are byte-identical to the previous derivation — that equivalence is the SSoT check and is pinned.
-
⚠️ THE DOMINANT COST WAS NOT THE ONE DIAGNOSED — and naming it correctly is the whole fix. The field diagnosis was "it should page only
node_ids, not decode each section's full graph". That is a real cost and it is fixed below. But profiling the actual scan found a larger, quadratic one sitting underneath it: the store catalog was re-derived once PER SECTION. A v12 HEAD-ONLY manifest deliberately stores no chromosome table (ADR-0003: "the catalog is derived, never a stored plaintext TOC"), sogenome_windowderives it by reading the entire body and re-folding its whole Merkle chain — andsection_countscalledgenome_windowper section. That is O(P × whole body): quadratic in corpus size, and at 240,881 sections it means re-reading a multi-hundred-MB body a quarter of a million times. Measured on the rc280 fixtures it was ~71 % of runtime at P=80 and growing, while per-section body size had almost no effect at all. Had only the targeted read landed, the op would still have been quadratic. -
Fix 1 — the catalog is derived ONCE per scan.
section_countsnow derives_catalog_dataa single time and pages every section against that already-derived entry (genome._region_leaves,genome._read_region_prefix), instead of re-deriving it inside the loop. Pinned structurally, not by wall-clock:test_catalog_is_derived_once_per_scan_not_once_per_sectionasserts the derivation count stays O(1) as P goes 4 → 8 → 16 → 32, so no timing flake can hide a regression. -
Fix 2 — the per-section read is TARGETED at
node_ids, with NO format change. The §89 graph-kernel payload is[vocab_size, n_node_ids] + node_ids + [n_extras] + extras + [n_edges] + edges…, so the label table is a strict PREFIX and the bulky edges sit strictly after it. Three existing on-disk properties make paging just that prefix sound, and none of them needed changing (GENOME_FORMAT_VERSIONstays 15): (a)quad_turnis a per-leaf reversible Klein-4 XOR bind — leaf k uncouples from leaf k alone, with no chaining — so a prefix of coupled leaves uncouples to exactly the prefix of the symbol stream; (b) the sections are self-describing, so the two-symbol length header lets the reader stop at the last complete int; © the region's integrity bound is its leading cap, the firstleaf_dimbytes, so a prefix re-hashes the samecap_sha256a whole-region read does. The targeted read is not a weaker read — it is the same bound over fewer bytes. Because path (1) sufficed, the index-chromosome design (a persisted accumulator as a peer to the shared__vocab__KERNEL chromosome) was NOT built: it would have added an on-disk shape to maintain and invalidate for a problem the format already solved. -
The read GROWS to fit rather than sizing for the worst case. A serialised int is 2 symbols of length header plus 1–15 base-4 digits, so a worst-case budget is ~3.4× larger than real id widths produce — big enough that it would page the whole region and save nothing (an early build did exactly that, reading 100 % of every region).
genome._section_node_idsinstead takes a one-leaf probe, recoversn_node_idsand measures this genome's actual symbols-per-int, then re-reads sized from that measurement with geometric growth as the backstop. Turn widths are estimated at the narrower packed form deliberately: under-reading costs one more bounded read, over-reading costs I/O on every section of every scan. Measured on corpus-shaped fixtures the scan now pages ~20–35 % of each region. It can never return a short table — the one failure mode that would silently under-count and shiftk— every path either satisfiesn_node_idsor has read the entire region and raises. -
Measured. Together the scan goes from O(P² × body) to O(P × node_ids). On the rc280 fixtures: 20.4× faster at P=80 and widening with P (6.0× at P=10 → 13.3× at P=40 → 20.4× at P=80 — the speedup grows precisely because the removed term was quadratic), with per-section cost flat at ~1.0 ms where the old path climbed 7.5 → 20.2 ms. Scaling shape is asserted structurally on bytes, not on seconds:
test_bytes_read_per_section_is_independent_of_edge_countbuilds two stores over identical documents differing only in co-occurrencewindow— the samenode_ids, a 2×+ larger edge payload — and asserts bytes read do not track the edge growth and stay strictly below the region. -
The technique generalized to two more quadratics; it did NOT generalize to the core harvest.
genome_integrate_plasmidswas re-deriving the catalog per section in two further places — the core-subgraph harvest and the section-strand fold — each independently O(P × body); both now share one derivation, as doesadd_plasmid. The node_ids-prefix technique, however, does not apply to the core harvest: that read needs the edges, because the edges are its payload. Reported rather than forced. -
New C symbol
srmech_genome_section_counts(ABI stays 6;GENOME_FORMAT_VERSIONstays 15). A bare-C host derives the counts end-to-end — catalog once, node_ids prefix per section, dedupe-per-section, ascending(id, count)output — per[[feedback_genome_must_exist_fully_in_c]]. Additive plain symbol reusing the existingsrmech_progress_tick_cb_ttypedef (no new callback typedef, so no ABI bump). Rather than mirroring Python's probe-then-grow re-read, the C peer streams the region through a sliding window into an incremental int decoder that stops the instant the declarednode_idsare in hand — same values, strictly fewer bytes read. Within-section dedupe is carried by alast_sectionfield on the open-addressed count table, so there is no per-section set or sort. On an output buffer too small it reports its TRUE required count vian_outand returnsSRMECH_ERR_OVERFLOW; the Python binding retries once at exactly that size. No malloc/goto/abs()/float; JPL-clean (≤60-line functions, ≥2 asserts); clean under ASan+UBSan. -
⚠️ HONEST LIMIT — the C peer does NOT reach corpus scale, and the pure path is what runs there. JPL Rule 3 bans malloc and the signature carries no
wsarena parameter, so the peer's working memory is three build-time-overridable file-scope statics (~38 MiB BSS): a 32 MiB catalog arena, a 2^18-slot count table (196,608 distinct ids), and a 64 KiB read window. The catalog arena's ~2.7 KiB/chromosome term puts the section ceiling at ~11,000 — well short of the F1253 store's 240,881. Over any of those bounds the peer returnsSRMECH_ERR_OVERFLOW, the binding reads that as a decline, and the pure Python path runs instead — which still gets both rc280 speedups, so the op is correct and fast at corpus scale, just not native there. Two consequences stated plainly rather than discovered later: the call is not reentrant (file-scope statics), andSRMECH_GENOME_SC_ARENA_BYTESis the build-time lever. Removing the ceiling outright would mean adding awsparameter — a wire-signature change, deliberately not taken in this rc. -
Defect found and fixed during the C build-out. The Python binding first passed the store path as a
(ctypes.c_char * len(path)).from_buffer_copy(path)buffer into aconst char *parameter — not NUL-terminated, i.e. an out-of-bounds read on the C side that happened not to crash. It now passes thebytesdirectly against actypes.c_char_pargtype, matching every sibling genome binding. Recorded rather than silently corrected. -
§101 progress/cancel — and cancel RAISES rather than returning a partial. The tick fires between whole SECTIONS with
phase=EXTRACTING. A truthy return raises the newSectionCountsCancelled(carrying.done/.total/.counts) instead of returning what it had. This differs deliberately from every other §101 op: a cancelled encode leaves chromosomes-so-far that form a valid, readable, shorter genome, but a partial count is not a smaller valid count — it is a wrong one, and every downstream read (conserved_core's antimode, the>= kpromotion) would silently derive a different threshold from an under-counted histogram with nothing in the result revealing it.progress=stays a Python-only kwarg, absent fromToolEntry.parameters. -
The rc279 fast path is unchanged and still the default.
genome_integrate_plasmids(section_count=…)consumes the free streamed accumulator with zero re-derivation — pinned by a monkeypatched tripwire assertingsection_countsis never called — andk_source(derived/declined/policy) semantics are intact. rc280 fixes the FALLBACK; it does not move work onto the hot path. -
Tests.
tests/test_plasmid_section_counts_rc280.py(18 tests): equivalence against the rc279 full-decode derivation and the streamed accumulator across five store shapes (including P=1 and vocab-saturating stores that force the prefix read to grow); per-section targetednode_ids== full-decodenode_ids; the VOCAB chromosome stays excluded; the catalog-derived-once ratchet; the bytes-read scaling shape; the growth loop never returning short; §101 tick shape and the raising cancel (including that a partial count can never exceed the full count); native == pure; and the rc279 fast-path/k_sourcenon-regression. numpy-free. -
Registry ripple. No new public callable —
section_countskeeps its name, category (plasmid) and wire parameters, sodescribe()["tools"]["total"]stays 456. Updated: thesection_counts+genome_integrate_plasmidsToolEntrysummaries (the latter carried the now-false "~0.33 s/section" claim) + the_tool_docsentry + regeneratedsrmech_tool_registry.c(CRLF;srmech_carrier_registry.cregenerated and verified byte-identical, as expected with no new callable and no carrier change).rosetta_classification.ndjson:section_countsmovesnon_compute/composes_c→c_dispatched(it now dispatches wholesale to its own C peer, theconserved_coreprecedent), so_TOTAL_NON_COMPUTE201 → 200 andcomposes_c129 → 128 across the three ratchet files — the debt moved DOWN: one fewer op a bare-C host must re-orchestrate itself. The down-onlyowedceiling is untouched at 0.
[0.9.0rc279]¶
STAGE 2 (ORGANIZE) — genome_integrate_plasmids, the incremental organize step (F1252 / §102). The second half of the two-stage genome encode (design: docs/srmech/notes/f1252_two_stage_encode_design.md). It consumes what rc278's stage 1 built and ORGANIZES it: CONSERVE (read the section-count distribution, DERIVE the threshold k) → PROMOTE (mint the induced core subgraph — the 0x58 centromere — via the rc277 mint_strand/G5 peer) → MERGE (fold the retained plasmid sections in via the rc276 integrate/G4 peer). This structurally removes the monolithic from-scratch partition from the encode path: stage 2 never calls recursive_cut, never re-extracts a document, and never runs a global spectral solve. Adding a document is a stage-1 append + an O(section) integer count bump + a re-mint of the small conserved core; every plasmid section, and the core when it did not change, stay byte-untouched.
-
New Python surface
srmech.amsc.plasmid.conserved_core(a new public callable) —kis DERIVED or DECLINED, never manufactured. Reads the{global_id: n_sections}accumulator and returns the conserved core + the threshold. Withk="auto"(the default;Noneis an accepted alias) it MEASURES the ANTIMODE of the section-count histogram — the same gap walk, the same qualifying predicate (a gap at least one bin WIDE with a real mode ≥ 2 nodes on EACH side) and the same widest-gap tie-break as the rc272 participation antimode (genome._partition_antimode), applied in the count domain, withk = lo + 1. Note the metric INVERSION vs participation: there HIGH participation = a community-bridging PLASMID; here HIGH section-count = shared across many plasmid sections = the conserved NUCLEAR core. The metric flips direction; the antimode discipline does not. Class-N exact integer arithmetic; no float, no division, noabs(), no spectral solve. -
⚠️ F1253 FIELD DATA — the real corpus curve is HEAVY-TAILED, so the derivation DECLINES on it, and that is the finding. A downstream session ran rc278
plasmid_extractover the FULL simplewiki corpus: 240,881 plasmid sections in 11.1 min (2.8 ms/doc, 336 MB, vocab 1,100,189) against the monolithicgenome_from_graph, which ran 8+ hours without finishing — stage 1 is validated at scale. It then measured the conservation curve that is exactly stage 2's promote input, over 1,100,189 ids: singleton 64.6 % | ≥2 35.4 % | ≥5 14.5 % | ≥10 8.4 % | ≥25 4.3 % | ≥50 2.7 % | ≥100 1.7 %. The successive ratios (≈2.4, 1.7, 2.0, 1.6, 1.6) decay smoothly — a heavy-tailed, near-power-law shape with no clean gap. A scale-free distribution has no characteristic scale and therefore no natural antimode, sokmay not be structurally derivable on the real corpus at all.conserved_corehandles this by DECLINING:k_source="declined",k=0, an EMPTY core, no chromosome promoted — it reports "no natural split in this distribution" rather than inventing a threshold. The decline path is tested as a first-class outcome (test_heavy_tailed_real_curve_declines_to_derive_a_k, on a fixture reproducing the measured quantiles). -
k_sourcemakes the threshold's provenance explicit — measured vs stated policy.k="auto"yieldsk_source="derived"when a qualifying antimode exists and"declined"when none does; an explicit integerkyieldsk_source="policy"— a stated policy choice the CALLER owns, never presented as measured. Nothing silently picks akand reports it as derived. Anti-numerology guard (tested): on the F1253 curvek>=5gives 14.5 % against F1251's attested ~16 % core, but that correspondence is threshold-dependent and is NOT howkis chosen — selectingkto reproduce ~16 % would be post-hoc numerology, not a derivation. The field session flagged this themselves and it is honored here: the curve is the deliverable, the ratio is not a target. -
⚠️ A REAL TENSION, RECORDED NOT PAPERED OVER (sharpens the design's F4 fermata). The two discriminators now disagree about whether structure exists at all: F1250's participation antimode DID find a bimodal split on the word graph, while the count-threshold discriminator finds NO natural split on the same corpus's section-count curve. That is not a convergence pending measurement — it is an observed divergence in what the two reads say. Possible readings (none adjudicated here): cross-DOCUMENT sharing and cross-COMMUNITY boundary mass are genuinely different structures; or the document boundary is the wrong section unit for conservation; or a scale-free count curve simply admits no threshold-shaped core and the F1251 core/accessory split must be recovered by a different (non-threshold) read. Resolving this is explicitly NOT claimed by rc279.
-
New Python surface
srmech.amsc.plasmid.genome_integrate_plasmids(a new public callable). The ORGANIZE op. Returns{strand, k, bimodal, one_dna_type, core, counts {nuclear, plasmid}, n_sections, n_integrated, histogram, status}, optionallygenome_save-ing toout_path. The organized genome censuses as ONEnuclearcore chromosome + the retainedplasmidsections — the biology-exact F1251 core/accessory shape. -
STAGE 2 CONSUMES THE STREAMED ACCUMULATOR BY DESIGN — pass
section_count.plasmid_extractalready returnssection_countas a free integer accumulator built during the append pass, so the normal stage-1 → stage-2 chain hands it straight through and stage 2 does zero count re-derivation. Omitting it falls back tosection_counts, which RE-DERIVES the counts by decoding every section body — a full O(P · section) re-scan measured downstream at ~0.33 s/section (hours at corpus scale). That fallback is the deliberate opt-in VERIFICATION path — for resuming against a store you did not just build, and for checking the accumulator against the on-disk SSoT — and must not be taken on the hot path: doing so would replace one slow monolithic step with another.core_edges=is the same bargain for the core harvest (supply the per-section global edge lists, asadd_plasmidmaintains them, and no section body is read at all). Theprogressaccounting reflects this: there is no scan phase on the fast path. -
New Python surface
srmech.amsc.plasmid.add_plasmid(a new public callable) — the INCREMENTAL step. Adds ONE document: stage-1 append → O(section) count bump → core delta (re-derivek, re-mint only the small conserved core). It threads a running organizestate({section_count, vocab, labels, core, k, sections});cache_edges=True(default) keeps the per-section GLOBAL edge lists instateso a threshold crossing re-filters in memory with no disk re-read (cost: O(|E|) resident), andcache_edges=Falsetrades that memory back for a store re-read on a core-membership change. Equivalence contract (pinned by the rc279 test):add_plasmidD times is BYTE-IDENTICAL to onegenome_integrate_plasmidsover the same D sections. The incremental rule is EXACT, not an approximation — the core subgraph sums per-section edge multiplicities and emits them in canonical sorted order, so it is independent of the order in which sections were accumulated. -
Two new C symbols (ABI stays 6;
GENOME_FORMAT_VERSIONstays 15).srmech_genome_conserved_coreis the CONSERVE peer (histogram build → antimode walk → core selection; a caller-arena histogram, pure integer cardinalities, no float / noabs()— a count has no sign to strip).srmech_genome_integrate_plasmidsis the ORGANIZE orchestrator: it callssrmech_genome_mint_strandfor the PROMOTE andsrmech_genome_integrateper section for the MERGE, so a bare-C host runs stage 2 end-to-end ([[feedback_genome_must_exist_fully_in_c]]). Becauseat < 0makesintegratea pure TAIL-APPEND, folding it over the P sections is exactly their concatenation (associativity) — the orchestrator therefore calls the peer at the running write offset and the whole fold is O(total) rather than the O(P · total) a literal re-splice of the growing host would cost; the width-coherence gate (a Class-K equality read, neverabs()) is applied per section so it is not vacuous against the empty host. Both are additive plain symbols;integrate_plasmidsREUSES the existingsrmech_progress_tick_cb_ttypedef (no NEW typedef) → ABI stays 6. Caller-arena; no malloc/goto/abs/float; JPL-clean (≤60-line functions, ≥2 asserts). -
§101 progress/cancel is C-HOST-REAL here. The tick is handed THROUGH to the C orchestrator, so the heartbeat and the cancel channel are the C loop's own — not a Python driver hook wrapped around an opaque call. This is the design's stated rc279 resolution of the parity-audit §4 concern that pipeline progress on Python-only orchestrators would bake a permanent C-host hole. It fires between whole CHROMOSOMES: once with
phase=MINTINGfor the core promote, then per section withphase=INTEGRATING(done= sections merged,total= P). A truthy return CANCELS cleanly — the returned strand is the minted core plus the sections merged so far, a valid readable shorter organized genome truncated at a chromosome boundary (itpartitions back, and is a byte-prefix of the full organize), andout_pathis NOT written.progress=stays a Python-only kwarg, absent fromToolEntry.parameters. -
⚠️ HONEST SCOPE — what is NOT claimed. The conservation criterion is a DIFFERENT discriminator from F1250's participation antimode, and NO convergence is claimed.
section_count >= kmeasures cross-DOCUMENT sharing; F1250's participation antimode measures cross-COMMUNITY boundary mass on the global graph. Their equivalence has not been established — and per the F1253 curve above the two reads currently disagree about whether a split exists at all — so stage 2 is not claimed to be a byte-equivalent refactor ofgenome_from_graph, and it is not claimed to reproduce the F1250 participation split. Nor is any wall-clock kill of the 3.4 h block claimed as measured. What IS proven here: the incremental organize replaces the from-scratch monolithic partition; the cost is O(|E|) incremental against O(D · |E| · log n) from-scratch; the shape is the biology-exact F1251 core/accessory split when a split exists; andkis derived from the data or honestly declined. The tests run on SYNTHETIC fixtures in BOTH regimes — a KNOWN planted core/periphery where the derivedkrecovers the planted split exactly, and a realistic heavy-tailed curve where the method must decline. -
Tests.
tests/test_genome_integrate_plasmids_rc279.py(22 tests) proves: the derivedkrecovers the PLANTED core on fixture A (periphery at count 1 →k == 2) and a DIFFERENTkon fixture B (periphery at count 3 →k == 4, soktracks the data rather than being a constant); the core is the asymmetric minority; a unimodal distribution is ONE-DNA-TYPE (no forced split); the F1253 heavy-tailed corpus curve DECLINES (k_source="declined",k == 0, no core minted,statusstillok— a decline is not an error),k_sourceseparates a MEASURED threshold from a STATED POLICY one, and the anti-numerology guard pins that the ~16 %-reproducingk>=5is reported aspolicy, neverderived; incremental == from-scratch byte-identically; the organize equals a literalgenome.integrateFOLD; native == pure byte-parity for both new peers, with an explicit anti-vacuity guard that the C orchestrator really dispatched; the census shape (1 nuclear + P plasmid); the fast path never callssection_counts(a monkeypatched tripwire) andcore_edgesskips the harvest read entirely; the slow fallback agrees with the accumulator (the SSoT check); and §101 monotone ticks + a clean chromosome-boundary cancel that stillpartitions. numpy-free (the whole file runs in a numpy-absent venv). -
Registry ripple. Three new public callables → three
ToolEntryrows (category="plasmid", matching the module path) + three_tool_docsentries + regeneratedsrmech_tool_registry.candsrmech_carrier_registry.c(the ops takethe_one, an HV, so the carrier op back-index shifted — the rc270/rc278 trap), both CRLF; threerosetta_classification.ndjsonrows —conserved_core→c_dispatched(a whole-op dispatch to a dedicated symbol, likemint_strand/integrate),genome_integrate_plasmids+add_plasmid→composition_of_c(genome BUILDERs over a whole-op C orchestrator, likegenome_from_graph/plasmid_extract);describe()["tools"]["total"]453 → 456 across the duplicated count-tests. Nonon_computerows → the_TOTAL_NON_COMPUTE/composes_cpins and the down-onlyowedceiling are UNCHANGED.progress=fires no ripple (a Python-only kwarg, no coercer).
[0.9.0rc278]¶
STAGE 1 (EXTRACT) — the plasmid-native sectional graph-L store (F1252 / §102). The first half of the two-stage genome encode (design: docs/srmech/notes/f1252_two_stage_encode_design.md). Each ingest DOCUMENT becomes ONE Tier-1 PLASMID chromosome — its LOCAL window co-occurrence graph, encoded to a §89/v6 KERNEL chromosome (Klein-4 leaves, a 0x6B kernel telomere, NO centromere) and APPENDED (§v12 O(1) HEAD-only) to a single sections/ genome store. This RETIRES the loose monolithic simplewiki_directed_sparse_kernel.json (~916 MB) at the graph-L layer: instead of dumping one JSON re-extracted every re-encode, adding a document appends ONE bounded plasmid section + bumps the head ([[feedback_persist_genome_native_not_loose_json]] one layer up). Stage 1 = WIRING three existing C peers, not a from-scratch build.
-
New Python surface
srmech.amsc.plasmid.plasmid_extract(a new public callable). Streamsdocs(an iterable of token sequences) into append-only plasmid sections: each document's LOCAL bounded co-occurrence (cooccurrence_topk— window / top-k / cap_slack) → a §89 KERNEL chromosome →genome_append(the first document SEEDS the store viagenome_save; the rest append O(1)). Edges use LOCAL node indices, but the section'snode_idslabel table maps each to a GLOBAL vocab id (via the append-onlyvocab) — so a word shared across sections carries the SAME id (the precondition stage-2 conservation reads); a shared VOCAB chromosome (the karyotype index) records the global word→id table. Returns{section_store, vocab (grown), section_count {global_id: n_sections}, n_sections, sections, status}.section_countis the integer accumulator (a node counts once per distinct section it appears in) stage-2 (rc279) promotes on.leaf_dim(len(the_one)) must be ≥ 52 (the §89 uniformly-Klein-4 kernel header fits one leaf). -
New Python surface
srmech.amsc.plasmid.section_counts(a new public callable). The genome-native, SSoT read stage-2 promotes on: derives{global_id: n_sections}by SCANNING the store's sections (paging each withgenome_window, decoding its GLOBALnode_idsviakernel_unpack+ the graph codec). The counts are the SSoT of the sections themselves (re-derived on read like the.faimanifest, never a loose sidecar); the streamed accumulator == the derived read, byte-for-byte. The VOCAB chromosome is excluded. -
New C orchestrator
srmech_genome_plasmid_extract(ABI stays 6; format stays 15). The genome-must-exist-in-C peer: it COMPOSESsrmech_graph_kernel_encode→ a NEW §89 KERNEL-region build (the missing syms→on-disk-region glue — a0x6Bkernel telomere cap + the coupled + §55/v3 bit-packed uniformly-Klein-4 header + content leaves, BYTE-IDENTICAL tokernel_pack+_disk_block) →srmech_genome_append, carving the encode syms buffer, the region buffer, AND the append arena from ONE callerws. The co-occurrence peer (srmech_text_cooccurrence_topk/_extract) is the other standalone stage-1 C peer a host composes upstream — socooccurrence_topk→plasmid_extractis the whole stage-1 stack in C, zero Python (a bare-C host extracts + appends a section end-to-end). Two new static helpers (genome_kernel_header_leafbase-4-packs D/leaf_dim/element_type;genome_v3_pack_turnbit-packs one coupled turn) mirror_pack_kernel_header_klein4/_pack_turn_blockbyte-exact. Pythonplasmid_extractDISPATCHES each section to it whenHAS_NATIVE; the pure body (_graph_kernel_encode+genome_append_kernel) is the numpy-free alternative + BYTE-PARITY oracle. Additive plain symbol reusing NO callback typedef → ABI 6 unchanged. NEVERabs()(an offset / a base-4 digit, no magnitude); no goto/malloc/float; caller-arena; JPL-clean (≤60-line functions, ≥2 asserts). -
Latent C bug fixed (debug-build only):
genome_hex2bytes'sassert(n <= 32u)widened to<= SRMECH_GENOME_LEAF_CAP(256). The helper is dual-use — 32-byte region-chain digests AND the manifestthe_one.hex(n ==leaf_dim, up to 256, bufferone_buf[256]). Release builds (NDEBUG) compiled the assert out soleaf_dim > 32worked; the debug C smoke tripped it the moment aleaf_dim ≥ 52kernel-section store was read (the first genome the tests exercise above 32). The fix matches the caller (aone_buf[256]decode) and the region-chain callers still pass exactly 32. -
§101 progress/cancel (rc275).
plasmid_extract'sprogress=is a Python-only kwarg (NOT an MCP wire param — a callable cannot cross JSON-RPC; the rc273 lesson, absent fromToolEntry.parameters). It fires between whole SECTIONS withphase=EXTRACTING(done=sections so far,total=n docs); a truthy return CANCELS cleanly, truncating at a valid chromosome boundary — the sections appended so far are complete chromosomes and NO partial section is left (status="cancelled"). -
C↔Python byte-parity + tests.
tests/test_plasmid_extract_c_rc278.pyproves: per-doc append → one plasmid chromosome; the store round-trips (census sees P plasmid sections + the VOCAB chromosome);section_countaccumulates + is SSoT-derivable; nativeturns.bin== pureturns.binbyte-for-byte (the C orchestrator's region == the puregenome_append_kernelregion); §101 cancel truncates at a section boundary; and RETIREMENT EQUIVALENCE — each stored section decodes back to the SAME co-occurrence graph the old JSON path (cooccurrence_topk) produced (genome-native, lossless). numpy-free (the whole file runs in a numpy-absent venv). The C smokec/test/test_srmech_genome.cadds an inlinesrmech_genome_plasmid_extractblock (seed → extract two sections → both page back cap-verified; NULL /leaf_dim < 52error paths). -
Registry ripple. Two new public callables → two
ToolEntryrows +_tool_docsentries + regeneratedsrmech_tool_registry.c; the ops takethe_one(an HV) so the HV carrier's op back-index grew → regeneratedsrmech_carrier_registry.c(the DUAL registry); tworosetta_classification.ndjsonrows (non_compute/composes_c, matchinggraph_to_kernel/genome_append_kernel);describe()["tools"]["total"]451 → 453 across the duplicated count-tests.progress=fires NO ripple (a Python-only kwarg, no coercer). ABI stays 6;GENOME_FORMAT_VERSIONstays 15 (the sections store is a plain genome dir of KERNEL chromosomes over existing v15 caps + blocks — a new directory usage, not a new on-disk shape).
[0.9.0rc277]¶
srmech_genome_mint_strand — the C peer for mint_strand, the stage-2 PROMOTE primitive of the F1252 two-stage encode (§100 GAP 1 / #891-peer / F1249 / G5). The next genome-must-exist-fully-in-C parity correction after rc276. mint_strand PROMOTES an already-packed Tier-1 PLASMID to a Tier-2 NUCLEAR chromosome by splicing a §95a interior centromere (0x58) at the p:q arm-split. Every PRIMITIVE it needs already had a C peer (srmech_genome_centromere cap, srmech_genome_recall, srmech_sha256_hex) — but the GLUE (data-turn scan → metacentric midpoint → single-block centromere insert) was Python-only, so its docstring's "byte-identical whether the cap came from C or pure Python" covered only the cap bytes, not the op. rc277 closes that gap: a self-contained C entry point lets a bare-C host PROMOTE a strand end-to-end, and the Python path DISPATCHES the whole op to it (native == pure, byte-identical). No new public callable — mint_strand already existed; the change is the C peer + a rosetta reclassification.
-
New C symbol
srmech_genome_mint_strand(ABI stays 6; format stays 15). Scans the strand's fixed-widthleaf_dim-byte DATA turns (a new one-passgenome_mint_strand_scancounts data turns AND flags an existing0x58, mirroring the Pythondata_positionsscan + the "already carries a centromere" guard), resolves the arm-split (centromere_at < 0= the metacentric midpointn_turns/2, the Pythoncentromere_at=Nonedefault), content-addresses the GLOBAL orientation from the strand's OWN recovered leaves whenorientation_auto(srmech_genome_recall→genome_mint_orientation=sha256(leaves)[0] & 3, the SAME Class-A→Class-C_mint_orientationrulemint()uses), writes the centromere cap (the existingsrmech_genome_centromere), and concatenatesstrand[:locus] + cap + strand[locus:]byte-identically (whole self-describing blocks — no re-coupling; the interior centromere is TRANSPARENT torecall/kernel_to_graph, which skip caps per §44). The recall step usesoutas its caller-arena scratch (the recalled leaves are≤ n_blocks·leaf_dim ≤ out_cap, FULLY consumed before the splice writesout) — no malloc. NEVERabs()— the split is a position and the orientation a content-address sector, neither a magnitude. Additive symbol, no new callback typedef → ABI 6 unchanged. -
Python
mint_stranddispatches to the C peer. WhenHAS_NATIVEand the strand is uniform fixed-width blocks (and the handle carries no NUL),mint_strandserialises the strand +the_one, calls the peer with the resolvedsplit+ theorientation/orientation_autoflag +repeats+ the UTF-8 handle bytes, and rebuilds the minted strand from its bytes — byte-identical to the pure block splice (the numpy-free fallback + parity oracle, and any non-uniform-width strand / NUL-or-over-long handle / bad orientation, which fall through so the exactValueErrorsurfaces). The §101progress=gate STAYS a Python-layer affordance (a splice has no meaningful partial; aCallablecannot cross the C wire — the rc273/rc275 lesson): it is checked in Python before the dispatch and is absent fromToolEntry.parameters(no new wire param,tools.totalunchanged at 451). -
Rosetta reclassification.
srmech.amsc.genome.mint_strandmovescomposition_of_c→c_dispatched(it now HAS a direct whole-op C peer, likeintegratein rc276). The docstring /ToolEntrynow point tosrmech_genome_mint_strandas the whole-op C peer (the prior text credited only thesrmech_genome_centromerecap-writer). -
C↔Python byte-parity + JPL. A differential test (
tests/test_genome_mint_strand_c_rc277.py) drives many strand shapes /centromere_atloci / explicit-and-content-address orientations / handles and asserts the native minted bytes == the pure minted bytes (0 mismatches), thatkernel_to_graphround-trips byte-exact after minting (the cap is transparent), plus a numpy-absent forced-pure run. The C smokec/test/test_srmech_genome.cadds an inlinesrmech_genome_mint_strandblock (content-address vs explicit orientation, the metacentric split, census → NUCLEAR, the already-minted / no-boundary-cap / OVERFLOW error paths). JPL Power-of-Ten clean (≤60-line functions, ≥2 asserts, no goto/malloc, noabs(), no float; caller-arena).
[0.9.0rc276]¶
srmech_genome_integrate — the C peer for integrate, the stage-2 SPLICE primitive of the F1252 two-stage encode (§95.1d / #891 / F1244 / G4). The first genome-must-exist-fully-in-C parity correction from the rc273 C-host audit. integrate's rc262/rc273 docstring CLAIMED "a C-only host integrates identically … region byte-offsets in the manifest", but the op ran on an in-memory strand (list of HV) with NO manifest at integrate-time — the boundary-scan → locus → splice orchestration was Python-only. rc276 closes that gap: a self-contained C entry point lets a bare-C host integrate a provirus into a host genome end-to-end, and the Python path DISPATCHES its splice to it (native == pure, byte-identical). No new public callable — integrate already existed; the change is the C peer + a rosetta reclassification.
-
New C symbol
srmech_genome_integrate(ABI stays 6; format stays 15). Scans the host's fixed-widthleaf_dim-byte blocks for chromosome-BOUNDARY caps (CHROM0x43/ kernel-telomere0x6B/ active-telomere0x74/ diploid0x44— the_CHROM_BOUNDARY_MARKERSset, via a newgenome_is_boundary_capreader overgenome_cap_kind), resolves the insert LOCUS fromat(the host chromosome index to insert BEFORE;at < 0= after the last, the Pythonat=Nonedefault), and concatenateshost[:locus] + provirus + host[locus:]byte-identically (whole self-describing blocks — the provirus turns are already coupled, so NO re-coupling). The DEFAULT compatibility gate is a Class-K coupling-WIDTH EQUALITY read — an empty host coheres with any provirus, elsehost_leaf_dim == prov_leaf_dim(two strands at different widths were coupled through differentthe_oneinvariants and cannot cohere: the CG258 incompatible-replicon analog). On incompatibility it HONEST-DECLINES (*integrated_out = 0, nothing written,SRMECH_OK— the C analog of the PythonNone; mirrorscentromere_of's*found_out = 0). NEVERabs()— an equality, not a magnitude. Additive symbol, no new callback typedef → ABI 6 unchanged. The faithful signature carries BOTHhost_leaf_dimandprov_leaf_dim(a justified refinement of the audit's single-leaf_dimshorthand) so the width gate is C-runnable on a bare-C host. -
Python
integratedispatches to the C peer. WhenHAS_NATIVEand the strand is uniform fixed-width blocks,integrateserialises host + provirus, calls the peer, and rebuilds the spliced strand from its bytes — byte-identical to the pure block splice (the numpy-free fallback + parity oracle, and any non-uniform-width strand). Thecompatible=predicate STAYS a Python-layer affordance (aCallablecannot cross the C wire — the rc273 lesson): it is checked in Python around the dispatch, and is absent fromToolEntry.parameters(no new wire param,tools.totalunchanged at 451). -
Rosetta reclassification.
srmech.amsc.genome.integratemovescomposition_of_c→c_dispatched(it now HAS a direct whole-op C peer). The rc262/rc273 "a C-only host integrates identically" claim in the docstring /ToolEntry/_tool_docsis now TRUE and points tosrmech_genome_integrate. -
C↔Python byte-parity + JPL. A differential test (
tests/test_genome_integrate_c_rc276.py) drives many host/provirus/atcombinations (plasmid / nuclear-centromere / diploid chromosomes; every locus; empty host; width-mismatch decline) and asserts the native splice bytes == the pure splice bytes (0 mismatches), plus a numpy-absent forced-pure run. The C smokec/test/test_srmech_genome.cadds an inlinesrmech_genome_integrateblock (everyatlocus + honest-decline + the error paths). JPL Power-of-Ten clean (≤60-line functions, ≥2 asserts, no goto/malloc, noabs(), no float; caller-arena).
[0.9.0rc275]¶
ENCODE PROGRESS + GRACEFUL ABORT — a C-native per-call heartbeat WITH a nonzero-return-to-CANCEL channel (§101 / PR#687 / F1252). The long srmech encode ops (recursive_cut, mint/genome, mint_strand, genome_partition, genome_from_graph, fiedler_sparse_file) were BLIND and un-cancellable. rc275 gives them an INLINE caller heartbeat + exact integer %-in/out + graceful abort — the libcurl XFERINFOFUNCTION / SQLite progress_handler / libgit2 transfer_progress pattern. It is C-native first (the primitive lives in the ABI, works on a bare-C / microcontroller host), with a Python(ctypes) trampoline + a co-equal pure-Python path honoring the SAME callback contract. This de-blinds the current interim run and lays the progress primitive the F1252 two-stage encode (rc276–rc279) reuses. FORM-matching only; the rc242 srmech_progress_cb_t dispatch-OBSERVER stays untouched and orthogonal (the separate trace-vs-progress-handler pattern).
-
New C primitive (ABI 5 → 6). A versioned event struct
srmech_progress_ev_t(first fieldstruct_size— the statx/Vulkan/cbSize gate; thenphase, exactuint64 done, exactuint64 total) passed by const-pointer to a NEW callback typedefsrmech_progress_tick_cb_treturningint(0 = continue, nonzero = CANCEL). Asrmech_encode_phase_tenum (NONE=0 /EXTRACTING=1 /INTEGRATING=2 /MINTING=3 /PARTITIONING=4; forward-extensible). A new clean-decline statusSRMECH_CANCELLED = 7(NOT an error — the C mirror of thetelomere_tickhonest-decline). Two additive*_progressoverload symbols —srmech_laplacian_fiedler_sparse_file_progress(tick per power-iteration; cancel →SRMECH_CANCELLED,out_vecleft zeroed = a valid "no cut" vector) andsrmech_genome_mint_progress(tick per kernel; cancel →*n_blocks_out= the COMPLETE chromosomes so far, a valid partial genome). The plain symbols keep their exact signatures and forward with a NULL tick. The new callback typedef is what bumpsSRMECH_ABI_VERSION5 → 6 (the #840 / v2→v5 CFUNCTYPE-wire precedent); later APPEND-only growth of the struct via itsstruct_sizegate will NOT re-bump. -
Python
progress=kwarg (in-process affordance; NEVER an MCP wire param). Aprogress(ev: dict) -> boolcallable onrecursive_cut/fiedler_sparse_file(laplacian) andgenome/mint/mint_strand/genome_partition/genome_from_graph(genome).ev={struct_size, phase, done, total}(exact ints; Class-N — the library never divides, never accumulates a float, neverabs()). Because aCallablehas NO JSON coercer (the rc273 macOS-red lesson),progress=is absent from everyToolEntry.parameters— no new public callable, nodescribe()["tools"]["total"]change, no carrier_registry / Rosetta ripple. Only the five registered ops' summaries document it. -
Cancel-return shapes (the derived clean partial / honest-decline). Dict-returning ops gain a
statuskey:recursive_cut→"cancelled"+ every still-pending set promoted to a coarse tome (the union still partitions allnnodes);genome_partition→"cancelled"+ the valid partial community assignment;genome_from_graph→"cancelled"+ the whole chromosomes minted so far, with NOgenome_save(nothing half-written hits disk). Bare-strand ops return the valid partial:mint/genome→ the complete chromosomes so far;mint_strand→ the UNMODIFIED pre-mint strand (a splice has no meaningful partial). Never a half-written unit; never a crash. -
The ctypes trampoline (the C-side rc273 Callable lesson).
_native._make_tick_trampolinewraps the Python callable so a raised exception NEVER crosses the C frames: it stashes the exception, returns 1 (a clean C-side cancel to unwind the loop), and the wrapper re-raises AFTER the C call returns; the CFUNCTYPE object is GC-pinned for the call's duration._native._ProgressEvis byte-identical tosrmech_progress_ev_t. -
C↔Python BYTE/BEHAVIOR parity + JPL. The native (trampoline) and pure paths emit the SAME
(phase, done, total)event sequence for the same input (differential-tested formintfull-sequence +fiedlercancel; monotone-and-reaches-100% forrecursive_cut); a numpy-absent forced-pure run honors the identical contract; the trampoline swallows a callback exception cleanly and re-raises. JPL Power-of-Ten clean (≤60-line fns, ≥2 asserts, no goto, no malloc, noabs(), no float in the progress math; thesrmech_progress_ev_tis a stack struct). A standalone C test (c/test/test_srmech_progress_tick.c) cancelsfiedler_file_iterateat iteration 3 (assertsSRMECH_CANCELLED+ zeroedout_vec) andgenome_mintat kernel 1 (asserts the valid partial). -
Doc-hygiene (folded in). Renumbered the rc274 G1 cell-state-chromatin work §102 → §98.1 (G1 extends the §98 chromatin access layer; §102 is reserved for the F1252 two-stage-encode arc) across the CHANGELOG / prototype note /
srmech_genome.cdocstrings /genome.py/_native.py/tool_schema.py/_tool_docs.py/ the rc274 test. Corrected the stale ABI references indocs/srmech/CLAUDE.md(was "v3" / "v2"; now v6 with the full v1→v6 progression).
[0.9.0rc274]¶
CELL-STATE-CONDITIONAL chromatin — facultative heterochromatin: accessible(strand, cell_state) is COMPUTED, not stored (§98.1 / G1 / PR#687). The rc268/rc269 0x48 chromatin ACCESS layer stored a STATIC accessibility level. G1 makes it cell-state-conditional — the Barr body / X-inactivation analog (facultative heterochromatin, H3K27me3 / Polycomb) — by reusing the EXISTING gene-gate machinery (klein4-mask / boolean-DNF / linear-threshold) ON the chromatin cap. WHICH regions are condensed is now a FUNCTION of cell_state. This is the op⊗operand theorem one gate OUTWARD from gene_express: SAME genome, DIFFERENT cell_state → DIFFERENT accessible open-set. FORM-matching only ([[user_stance_cascade_matching_substrate_blind_form_not_identity]]); biology not superseded.
-
New public op
accessible(strand, cell_state, *, the_one=None) -> (num, den). Scans for the FIRST interior0x48chromatin cap and returns its COMPUTED accessibility level. A CONSTITUTIVE cap (the pre-rc274 default; centromeric / telomeric H3K9me3 heterochromatin) reads its STATIC stored level, CONSTANT incell_state. A FACULTATIVE cap (a gate set viacondense(state={...})) reads its WHEN-OPEN level iff its gate FIRES undercell_state(the SAME §129/§130/§131 evaluators applied to the cap), else(0, 1)(silenced). A chromatin-FREE strand reads(1, 1)(default euchromatin).num > 0is "open" (Class-K sign; neverabs()). A READ — never mutates. -
Facultative
condense(state=dict)— the writer side.condense(..., state={...})now accepts a FACULTATIVE dict alongside the constitutiveTrue/False/"open"/"condensed"/(num,den)forms:{"activator": m, "repressor": m0}(klein4, §129 E1),{"dnf": [(act, rep), …]}(boolean, §130 E2), or{"weights": [w, …], "threshold": t}(threshold, §131 E4), each with an optional"open_level"(the WHEN-OPEN accessibility, default(1, 1)). The chromatin access level then composes MULTIPLICATIVELY with a graded promoter ingene_express_levels. The rc269 demand-load PATH plan (gene_express_plan+genome_genes_expressed) and the four cell-state read-sites (gene_express/gene_express_levels/ the STRAND + PATH plans) are now cell-state-conditional — a state-CLOSED facultative region is still SKIPPED at plan time having touched ONLY the chromatin cap (the cell-state gate rides IN the already-paged cap, so the rc269 bounded-I/O SINGLE-SEEK is preserved). -
Byte format — additive, no bump (mirrors the rc273 copy-number / rc129 repressor dual-read). An additive
access_gate_type(u8)field is written afterden, in the cap's existing NUL padding, reusing the gene-gate wire forms (KLEIN4act+rep; BOOLEANn_terms + terms; THRESHOLDn_weights + threshold + weights— NO inner gate_type byte,access_gate_typeis the discriminator).access_gate_type == NONE (0) ==the pre-rc274 pad byte, so a CONSTITUTIVE cap is BYTE-IDENTICAL to a pre-rc274 v15 cap, and a gate-blind / pre-rc274 reader degrades a facultative cap to its when-open (active) level — the biologically-correct default.SRMECH_GENOME_FORMAT_VERSIONstays 15 (additive-in-padding, no new marker). k=3 codon-radix is NOT touched (no new marker, no new symbol quantization). -
C↔Python BYTE-parity + JPL. Two new C symbols:
srmech_genome_chromatin_access(the single-cap COMPUTED reader — the demand-load head-resolvergenome_plan_read_headnow evaluates the variable-length facultative gate from the single already-paged cap, malloc-free, in-place, byte-identical, preserving the single-seek) andsrmech_genome_chromatin_gated(the facultative cap writer — appends the Python-serialized gate blob verbatim). Both additive →SRMECH_ABI_VERSIONstays 5. Differential-tested native==pure foraccessible(∀ cell_state), the writer (raw bytes), and the demand-load PATH plan (∀ cell_state on a MIXED genome). The C reuses the existinggenome_read_u64_be/genome_read_i64_bereaders; JPL Power-of-Ten clean (≤60-line fns, ≥2 asserts, no goto, no malloc, noabs(), no float). -
Registry ripple. ONE new public callable →
describe()["tools"]["total"]450 → 451: newaccessibleToolEntry+_tool_docsentry + the regeneratedc/src/srmech_tool_registry.c+c/src/srmech_carrier_registry.c(hash-ratchets re-lock) + a Rosettacomposition_of_crow +test_mcp.py(allaccessibleparam types are wire-serialisable —Sequence[HV]/int/HV, no callable). The three non_compute pins are untouched (accessibleis a compute op with a C peer). New tests:python/tests/test_cellstate_chromatin_rc274.py(8 shapes: constitutive/plain invariance; facultative tracks cell_state for klein4/boolean/threshold; back-compat byte-identity + format 15; C↔Python parity for accessible/writer/plan; per-state demand-load skip with bounded single-seek; save/reload/decondense/integrate survival; read-only; graded-facultative × graded-promoter multiplicative composition). Attests facultative heterochromatin: X-inactivation / the Barr body — Chadwick & Willard 2004, PNAS 101:17450 (PMC534659, OA); constitutive-vs-facultative — Brown, Genomes (NBK21137, OA).
[0.9.0rc273]¶
Two biology-native genome refinements from F1251 (attested Shropshire bacterial genomics) — horizontal transfer has EMPIRICAL BOUNDARIES, and amplification stores a COPY NUMBER (§95.1d / §135 / F1244 / F1251). F1251 mapped MPM-verified bacterial genomics onto the framework and surfaced two things the genome ALREADY IS structurally that the tooling did not yet read: (1) plasmid sharing is NOT universal — CG307 plasmids are shared with other clonal groups EXCEPT CG258, which stays segregated — so integrate must be able to REFUSE; (2) IS26-mediated amplification raises a gene's COPY NUMBER — the genome stores "how many copies", not just presence. Both are organization/algebra-side reads (defensive scope, no biochemistry).
-
PART A —
integrategets a COMPATIBILITY GATE (F1251 / F1244).integrate(host, provirus, *, at=None, compatible=None)now CHECKS host↔provirus compatibility BEFORE splicing and HONEST-DECLINES on incompatibility — it returnsNone(a clean refuse, inform-don't-crash, mirroringtelomere_tick's senescence) and leaves the host UNCHANGED, instead of forcing every element into every host. The DEFAULT predicate (_integrate_coheres) is the §95.1d/F1244 coherence contract made checkable: host + provirus must share the coupling WIDTH (== thethe_one/leaf_dim), because two genomes coupled at different widths were coupled through DIFFERENT invariants and cannot cohere — an incompatible replicon (the CG258 segregated-lineage analog). This is a Class-K/C equality read (are the two coupling widths equal?), NEVER a magnitude (abs()is not used). Pass an explicitcompatible=(host, provirus) -> boolhook to add a domain replicon-/lineage-compatibility barrier (a same-width but different-lineage CG258 case); it is checked IN ADDITION to the width coherence (both must pass). Full back-compat: a COMPATIBLE provirus integrates EXACTLY as rc262 (the gate adds ONLY a refuse path; an empty host still returns the bare provirus). The advertised return type becameOptional[list]. -
PART B — gene COPY-NUMBER (multiplicity) —
amplify+copy_number_of(§135 / F1251).amplify(chrom, label, n)records a gene's COPY NUMBER on the named plain gene's0x47cap, andcopy_number_of(chrom, label)reads it back. Copy-number is a multiplicity annotation (a Class-I/N exact integer count) — NOT N physical duplicated strands, so the strand LENGTH is unchanged. Encoding (mirrors the §127 active-telomere count / §129 regulatory masks): auint64big-endian field carried RIGHT AFTER the label's NUL, in what was the gene cap's NUL padding — so a plain gene / a pre-rc273 genome (all-NUL padding == stored 0) reads as copy-number 1 (present-once, back-compat), and onlyn >= 2spends the 8-byte field (n == 1is BYTE-IDENTICAL to a plain gene, the §129 dual-read discipline one field over). The count is TRANSPARENT to every existing reader —gene_express/partition/recall/genome_censusread a0x47cap as ALWAYS-EXPRESSED regardless of the trailing bytes, in both Python (_gene_expresses) and C (srmech_genome_gene_expressreturns on the0x47marker before reading any field) — and survivesgenome_save/ reload /integratebyte-exact. -
No format / ABI change (justified). No new marker; the copy-number is an ADDITIVE field in existing gene-cap NUL padding, read bidirectionally back-compatibly (v≤15 reads a copy-number gene as a plain always-express gene; a v273 reader reads a pre-rc273 gene as copy-number 1).
SRMECH_GENOME_FORMAT_VERSIONstays 15;SRMECH_ABI_VERSIONstays 5 (no new C symbol — both ops are pure composition over the C-built strand, likemint_strand;integratehas no C peer). C smoke (test_srmech_genome.c) adds the C forward-compat proof (a plain0x47cap carrying a copy-number field reads always-express). -
C↔Python parity + registry ripple.
amplify/copy_number_ofare pure Python composition (a cap rewrite / read + byte-copy) — Rosettacomposition_of_c(byte-identical whetherchromcame from the native or pure builders). TWO new public callables →describe()["tools"]["total"]448 → 450: newToolEntrys +_tool_docsentries + the regeneratedc/src/srmech_tool_registry.c+c/src/srmech_carrier_registry.c(SHA-256 hash-ratchets re-lock) + Rosettacomposition_of_crows; the integrateToolEntrysummary/params updated for the gate. The three non_compute pins are untouched (both new ops are compute-sidecomposition_of_c, not non_compute). New tests:python/tests/test_genome_copy_number_amplify_rc273.py(Part B: amplify→copy_number_of, plain gene reads 1, count survives save/reload + integrate, gene_express transparency, backward-compat, n==1 byte-identity, native==pure) andpython/tests/test_genome_integrate_gate_rc273.py(Part A: compatible integrates as rc262, incompatible width honest-declines with host unchanged, thecompatiblehook barrier, empty-host). JPL Power-of-Ten unaffected (no new C beyond the smoke; noabs(), no float — the gate is a Class-K equality, the count a Class-I/N integer).
[0.9.0rc272]¶
Partition a genome by the DATA'S OWN STRUCTURE — genome_partition + genome_from_graph (§100 GAP 2 / PR#687 / F1250 / F1251). Today mint/mint_plan decide a chromosome's shape by leaf-count (≤4-leaf → plasmid, ≥5-leaf → nuclear). §100 GAP 2: for a directed relational GRAPH the builder must instead find the data's OWN nuclear-core vs plasmid-periphery split from its relational structure — biology does not size a chromosome by how many genes it has; it reads the community topology (a small stable clonal CORE + a large mobile ACCESSORY genome; F1251, attested bacterial genomics).
genome_partition(n, edges, weights=None, charges=None, *, work_dir=None, max_tome=256, n_bins=16, max_iters=250)— an introspectable READ that BUILDS NOTHING (likemint_plan, "we watch it happen"). Runs the out-of-core spectral community partition (laplacian.recursive_cut→ the C-dispatchedfiedler_sparse_file; peak RAM = the largest sub-graph, NEVER the dense n×n structure), measures each node's degree-normalized PARTICIPATION = the fraction of its incident edge-mass that CROSSES a community boundary (HIGH = a community-bridging PLASMID/mobile accessory, F1059's power source; LOW = a NUCLEAR node embedded in ONE community, the stable topical core), then MEASURES the antimode of that distribution.- Metric = participation, NOT the clustering coefficient. §100.1/F1250 MEASURED that clustering is unimodal at the 831k word-graph scale and washes out — participation is the read-INDEPENDENT discriminator that survives.
- Decision = the antimode gap. Genuinely BIMODAL (a clean, wide, empty-relative valley:
2·valley < min(peak_low, peak_high)) → SPLIT into nuclear (low) + plasmid (high); UNIMODAL (no clean antimode) → ACCEPT ONE-DNA-TYPE, do NOT force a split (F1250: the word graph is "one bridged content mass"). The split is per-NODE (robust even whenrecursive_cutabsorbs a bridge node into a community — a bridge still crosses to the other community); each community is then sliced into its embedded-core nuclear group + its bridging plasmid group. - Shape = an ASYMMETRIC minority nuclear core + majority plasmid remainder (F1251), never forced 50/50.
weightsare the INTEGER edge metric so participation is an exact rational (Class-N; no float, neverabs());charges(signed direction) ride through for the builder. Returns{n, n_communities, bimodal, one_dna_type, antimode:{bins,counts,threshold_bin,peak_low_bin,peak_high_bin,valley_count,gap}, participation:[(num,den) per node], communities, groups:[{community,type,nodes,size,participation}], counts:{nuclear,plasmid}, node_counts, work_dir}. genome_from_graph(n, edges, weights=None, charges=None, *, the_one, path=None, leaf_dim=None, max_tome=256, n_bins=16, centromere_at=None)— the BUILDER: "hand a graph, get nuclear + plasmid from its structure". Runsgenome_partition, then for EACH group packs its INDUCED sub-graph (graph_to_kernel) into a chromosome — a nuclear community is MINTED (mint_strandsplices a0x58centromere → a Tier-2 nuclear chromosome), a plasmid community is KEPT as a Tier-1 plasmid chromosome. All chromosomes concatenate into one self-describing strand; ifpathis given,genome_save+genome_censusreport the MEASURED{nuclear:N, plasmid:M}. BYTE-EXACT per community:kernel_to_graphon any chromosome (with itsn_syms) recovers that community's induced sub-graph exactly (the interior centromere is skipped on read, §44).- C↔Python parity — composition-of-C, native==pure. Both ops compose the C-dispatched
recursive_cut/fiedler_sparse_file+graph_to_kernel+mint_strandwith a thin PURE exact-integer participation + antimode read (O(|E|) + O(n)). No NEW C symbol (the numeric heavy lifting is already C); the whole composition is native==pure because the participation/antimode read is deterministic over the native community assignment (proven on the clean bimodal graph). Rosetta: both classifiedcomposition_of_c.
No format / ABI change: no new marker, SRMECH_GENOME_FORMAT_VERSION stays 15, SRMECH_ABI_VERSION stays 5. TWO new public callables → describe()["tools"]["total"] 446 → 448 (new genome_partition + genome_from_graph ToolEntrys + _tool_docs entries + the regenerated c/src/srmech_tool_registry.c + c/src/srmech_carrier_registry.c hash-ratchets + Rosetta composition_of_c rows; the three non_compute pins are untouched — both ops are compute). JPL Power-of-Ten unaffected (no new C; no abs(), no float — participation is a Class-N rational, the antimode a deterministic integer measure). New tests: python/tests/test_genome_partition_rc272.py (clean-bimodal split with cliques nuclear + bridges plasmid + asymmetric-aware; unimodal accept-one-DNA-type; exact-rational participation; the antimode measure; native==pure; the builder round-trip census + per-community byte-exact kernel_to_graph incl. after genome_save; registration). rc273 queue: a dedicated C srmech_graph_participation numeric kernel is available-if-wanted (the participation accumulation currently rides pure integer glue over the C community assignment, the established mint_strand composition-of-C precedent).
[0.9.0rc271]¶
Genome vocabulary → the field's own biology names (BREAKING at the value level) + a VALUE-ALIAS opt-in (§96 / F1251 / PR#687). F1251 (attested Shropshire bacterial genomics) confirmed our coined §96 cap_kind names ARE the field's: the ACCESSORY / mobile genome is the plasmid, the CORE / clonal genome is nuclear. "Science is the SSoT of science" → this rc adopts the field's names as canonical and ships the old srmech names as an opt-in alias.
- PART A — the rename (BREAKING). The DERIVED per-chromosome
cap_kind/ census type VALUES change everywhere:"stick"→"plasmid"(Tier-1, mobile, append-only, no centromere) and"minted"→"nuclear"(Tier-2, stable, centromere-anchored);"diploid"is unchanged (already field vocabulary). This is the string VALUES + the censustypesdict KEYS ({plasmid, nuclear, diploid}) ingenome_catalog/genome_census/genome_registry,mint_plan'sshapelabel, and thesrmech_genome_genome.cmapper (genome_cap_kind_str→"plasmid"/"nuclear"; the internal#defines renamed…_STICK/_MINTED→…_PLASMID/_NUCLEAR). Thetopologyreads (nuclear-like/organelle-like/plasmid/prokaryote-like) are unchanged (they were already field-native). - NOT a format change.
cap_kindis DERIVED on read (rc267), never stored on disk — so there is NO on-disk migration and NO format / ABI bump:SRMECH_GENOME_FORMAT_VERSIONstays 15,SRMECH_ABI_VERSIONstays 5, no new marker. A pre-rc271 v15 genome catalogs / censuses identically except the two renamed strings (its bytes read unchanged; only the derived label differs). C↔Python 1:1 parity holds at the canonical level (nativegenome_census/cap_kindreturnplasmid/nuclear, and native==pure). - PART B — a VALUE-ALIAS presentation layer (opt back into the old names). A user whose domain prefers the old srmech names — or any other vocabulary — installs a canonical→preferred mapping applied as a pure Python PRESENTATION layer OVER the canonical output of
genome_census/genome_registry/genome_catalog(thecap_kind/typefield VALUES and the censustypesdict KEYS). The C layer + on-disk format stay canonical only (plasmid/nuclear); the alias is the SAME uniform post-transform on BOTH the native and the pure result, so native==pure still holds at the canonical level and the alias never touches storage / format / ABI. Three new public callables onsrmech.amsc.genome:set_type_aliases({"nuclear":"minted","plasmid":"stick"})/clear_type_aliases()(programmatic; session-global, default = canonical) andload_type_aliases_toml(path)(reads a[genome.type_aliases]TOML table via the Csrmech_tomlparser — the rc261load_aliases_tomlshape — installs it, and returns the mapping). A documented example restoring the oldstick/mintednames ships atpython/tests/data/genome_type_aliases_legacy.toml(a[genome.type_aliases]table). - Registry ripple (rosetta-only, mirroring rc261's
dsl/_alias.py). The three value-alias callables are config / presentation dev-affordances (a bare-C host emits only the canonical output and never re-presents it), so — exactly like rc261's function-alias binder — they are classified inrosetta_classification.ndjson(set_type_aliases/clear_type_aliases→dev_tooling;load_type_aliases_toml→composes_c, it parses the TOML via C) and are NOT added to the MCP tool surface:describe()["tools"]["total"]stays 446 (noToolEntry, no carrier-registry renumber). The tool-schema genome SUMMARIES +_tool_docsexplanations were updated to the new vocabulary (a doc-text change to existing tools), soc/src/srmech_tool_registry.cwas regenerated (the live hash-ratchet re-locks). Ratchet counts:_TOTAL_NON_COMPUTE197 → 200,composes_c127 → 128,dev_tooling49 → 51 (allowlist +2). New tests:python/tests/test_genome_type_vocab_rc271.py(the rename correctness, the value-alias round-trip + example TOML, backward-compat of a pre-rc271 v15 genome, native==pure at the canonical level). JPL Power-of-Ten unaffected (the renamedgenome_cap_kind_strstays ≤60 lines / ≥2 asserts; no new C symbol, noabs(), no float).
[0.9.0rc270]¶
mint_strand — MINT an ALREADY-PACKED strand (§100 GAP 1 / PR#687 F1249). The corpus directed-graph store built with graph_to_kernel could NOT be given a p:q centromere: graph_to_kernel returns an HV-strand, and chromosome(strand, centromere=…) REJECTS it — it treats the already-packed strand as raw leaves and quad_turn binds the 256-sector telomere cap → "klein-4 elements must be in {0,1,2,3}" — and there was no centromere= hook on graph_to_kernel. So a directed-graph chromosome stayed a Tier-1 stick (simplewiki_directed.genome censused {stick: 2, minted: 0}). This rc ships the missing capability as ONE new public op genome.mint_strand.
- The op.
mint_strand(strand, the_one, *, orientation=None, centromere_at=None, repeats=15, handle="cen")splices a §95a interior CENTROMERE cap (0x58) into an already-packed strand at the p:q arm-split, turning a Tier-1 STICK into a Tier-2 MINTED chromosome — WITHOUT re-minting it from leaves. Works on ANY packed strand: agraph_to_kernel/kernel_packstrand, anychromosome, or one nuclear community (the foundation for §100 GAP 2 — mint each nuclear community — and the streaming reader, where a minted chromosome IS the eukaryotic/nuclear DNA).mint()mints AT BUILD TIME from raw leaves;mint_strandmints POST-PACK. kernel_to_graphstays BYTE-EXACT. The centromere is an INTERIOR cap;recall/kernel_unpack/kernel_to_graphALL skip caps (§44), so minting is TRANSPARENT to the payload — the recovered{vocab_size, edges, weights, charges, node_ids, extras}is byte-identical with or without the centromere (proven on a mixed directed / signed-charge / node_ids / extras graph, in-memory AND aftergenome_save). After minting,genome_save+genome_censusreport the chromosome asminted(topologynuclear-like).- The p:q convention.
centromere_atis the arm-split measured in DATA TURNS (default the metacentric midpointn_turns // 2— the SAME defaultchromosome(centromere=…)uses; a 9-turn strand mints(4, 5), a 30-turn strand(15, 15)). POSITION IS the p:q arm-ratio (centromere_ofreads it back).orientationis the global 4-way which-way0..3(default the content-address foldsha256(recovered leaves)[0] & 3— the SAME Class-A→Class-C rulemint()assigns via_mint_orientation, so on a plain chromosomemint_strandis byte-identical tochromosome(leaves, centromere=that orientation)). - Composition, not a new C op — proven byte-identical.
mint_strandcomposes over the native-dispatchedcentromere()cap-writer (byte-identical C peersrmech_genome_centromere, shipped rc258) + a PURE strand splice (self-describing blocks concatenated, no re-coupling — likeintegrate), so the minted strand is byte-identical whether the cap came from C or pure Python (native==pure parity test). A C-only host mints a graph strand identically (encode → pack → splice the centromere cap → the decode stays byte-exact; the extendedc/test/test_srmech_genome.cproves the C centromere cap + graph decode round-trip).
No format / ABI change — reuses the existing 0x58 centromere marker: no new marker, SRMECH_GENOME_FORMAT_VERSION stays 15, SRMECH_ABI_VERSION stays 5 (no new C symbol — mint_strand is a pure composition over srmech_genome_centromere). ONE new public callable → describe()["tools"]["total"] 445 → 446 (new genome.mint_strand ToolEntry + _tool_docs entry + the regenerated c/src/srmech_tool_registry.c hash-ratchet). JPL Power-of-Ten unaffected (no new C function; no abs(), no float — the p:q is a Class-N rational, the orientation Class-C/K). New tests: python/tests/test_genome_mint_strand_rc270.py (the rejection cause, byte-exact-after-mint, census-minted, metacentric (4,5)/(15,15), generality over kernel_pack + chromosome strands, the error contract, backward-compat, registration, native==pure cap parity) + the extended C smoke.
[0.9.0rc269]¶
Chromatin gates the DEMAND-LOAD PATH plan — completing the rc268 deferral (§98 / #1422 / F1247 / PR#687). rc268 wired the chromatin OUTER gate (expressed = accessible(region) AND promoter(gene)) into gene_express, gene_express_levels, and the in-memory STRAND gene_express_plan variant, but explicitly deferred the demand-load PATH variant: the disk plan (_gene_express_plan_path + its C peer srmech_genome_gene_express_plan) still read every region's head gene gate cap regardless of chromatin. This rc closes that gap — the bounded-I/O win §98/F1247 was really after.
- The change. At each region head the plan now recognizes a HEAD CHROMATIN cap (
0x48) in the slot right after the CHROM cap (the region-head layout[CHROM cap][chromatin cap?][gene gate cap][data…], exactly wherecondense(region=None)splices it). Heterochromatin (condensed) ⇒ the whole region is SKIPPED at plan time having touched ONLY the chromatin cap — its gene gate cap is NEVER read (even fewer bytes-touched than the §134 read). Euchromatin (open) ⇒ advance one slot and evaluate the gene gate atoff + 2·leaf_dimas before. The accessible/silenced decision is the SAME Class-K predicategene_expressand the STRAND plan use —access_open = level_numerator > 0(BINARY condensed(0,1)and GRADED level-0both silence; neverabs()). - A chromatin-FREE region is byte-for-byte the rc135 read — the plan is unchanged (backward-compat asserted), so no existing genome's plan moves.
- C↔Python 1:1 byte-parity. The native and pure PATH plans return the IDENTICAL
[(label, byte_offset, byte_len), …]on a mixed genome (condensed + open + chromatin-free), proven by a native==pure parity test. The STRAND plan (rc268) and PATH plan (rc269) agree on the expressed label set.
No format / ABI change — this is a READ behavior change only: no new marker, no wire-format change to srmech_genome_gene_express_plan, SRMECH_GENOME_FORMAT_VERSION stays 15, SRMECH_ABI_VERSION stays 5. No new public callable (it extends gene_express_plan's behavior) — describe()["tools"]["total"] stays 445; the gene_express_plan ToolEntry / docstring gain the chromatin-skip note. JPL Power-of-Ten holds (the new C helper genome_plan_read_head is ≤60 lines / ≥2 asserts / no goto/malloc/multi-line-macros; no abs(), no float). New tests: python/tests/test_express_plan_chromatin_rc269.py (bytes-touched proof, native==pure parity, euchromatin-defers-to-promoter, backward-compat, STRAND-vs-PATH agreement) + an extended C smoke c/test/test_srmech_genome.c PATH-plan chromatin-skip check.
[0.9.0rc268]¶
The CHROMATIN ACCESS LAYER — biology's epigenetic packaging gate (§98 / #1422 / F1246-F1247 / PR#687). The per-region euchromatin(accessible) / heterochromatin(silenced) marker that gates WHICH regions express, on-demand — biology's modify-WITHOUT-changing-the-DNA layer, sitting ABOVE the coupled-turn content and the §128–132 gene promoters. ONE new interior cap-marker 0x48 ('H' = histone/heterochromatin — the 13th structural marker, verified unused among the prior 12; the marker alphabet stays byte-per-layer, no codon-frame refactor), spanning BOTH granularities and BOTH states:
- The primitive. A
CHROMATIN_MARKER(0x48) interior cap (like the §95a centromere0x58, it never OPENS a chromosome) carries an accessibility LEVEL inline:[0x48] + handle + NUL + chromatin_type(uint8) + num(uint64 BE) + den(uint64 BE). The level is the reduced non-negative rationalnum/denin[0,1](Class-N exact; NO float, NEVERabs()— a level is a non-negative fraction): BINARY (type 0) carries(1,1)OPEN /(0,1)CONDENSED; GRADED (type 1) an arbitrary reduced rational (partial accessibility, the graded-gene0x64rational layout reused). PLACEMENT is scope: right after the opening telomere (0 data turns before it) → whole-chromosome (the X-inactivation / master case); deeper interior → a sub-region STRETCH. - The ops — IN-PLACE, no re-mint (load-bearing).
condense(strand, state=…, region=…, label=…)SPLICES the marker in anddecondense(…)splices it out, PRESERVING the centromere + body sequence: a MINTED chromosome condensed-then-decondensed is byte-identical to the original mint (the0x58centromere byte-identical,centromere_ofunchanged) — NO re-mint.chromatin_of(strand)→{type, state, level:(num,den), handle, at, scope}orNone(an all-euchromatin, fully-accessible chromosome — the default).state=True/"condensed"(silenced),False/"open"(accessible), or a(num,den)graded level;region=None(whole chromosome), an int data-turn index, or a gene-label str (a stretch). - The OUTER gate —
expressed = accessible(region) AND promoter(gene, cell_state). Wired intogene_express,gene_express_levels, and the STRANDgene_express_planvariant: heterochromatin silences a region even when its promoter would fire; euchromatin falls through to the §128–132 gate; a GRADED chromatin level composes multiplicatively with the §132 graded-gene level as an exact rational (accessibility × promoter-level;_compose_levels, Class-N/I, neverabs()). rc269 follow-up (explicitly deferred): the PATH demand-load single-seek chromatin skip in the C peersrmech_genome_gene_express_plan— the STRAND plan variant IS wired; the disk-plan bytes-touched skip is not. - Round-trip + survival. Chromatin marks survive
genome_save/ reload andintegrate(likecentromere_of, §95). A chromatin-free genome is unaffected (all-euchromatin default). The chromatin cap is registered in the scanner /genome_cap_kind/genome_block_len/ census derivation, so leaf counts and the stick/minted/diploid classification stay correct (a chromatin cap is a region-access marker, orthogonal to the census type — census output is unchanged). - Format v14 → v15 — additive + backward-compatible. A chromatin-FREE genome reads identically (a v15 writer stamps 15; back-compat is STRUCTURAL, so v≤14 bodies read UNCHANGED — the walker gains ONE branch).
SRMECH_GENOME_FORMAT_VERSION+ thegenome_persistence/v15+genome_chromosome/v15parser_rule_hashpre-images bump in lockstep (C ↔ Python).
Two additive C symbols (srmech_genome_chromatin cap writer + srmech_genome_chromatin_of strand read), byte-identical to the pure _pack_chromatin / chromatin_of oracles (native==pure parity tests). Three new genome-category ToolEntrys (condense / decondense / chromatin_of; describe()["tools"]["total"] 442 → 445), _tool_docs entries, Rosetta composition_of_c rows, and MCP coercers for the state / region union types. Additive symbols only — SRMECH_ABI_VERSION stays 5. JPL Power-of-Ten holds (every new C function ≤60 lines / ≥2 asserts / no goto/malloc/multi-line-macros). New tests: python/tests/test_genome_chromatin_rc268.py + the extended C smoke c/test/test_srmech_genome.c (11 chromatin checks).
[0.9.0rc267]¶
Genome introspection, biology-native — per-chromosome cap_kind + genome_census + genome_registry (§96 / PR#687 UPSTREAM_NOTES). srmech reads the SHAPE (the inline cap markers, classified once in the §44 body scan); the caller assigns the ROLE. Three additive deliverables, C↔Python 1:1:
- (A)
cap_kindin the catalog. Every chromosome entry fromgenome_catalog(Python) andsrmech_genome_catalog(C) now carries acap_kind∈{"stick","minted","diploid"}, DERIVED on the EXISTING body scan (no extra pass, nothe_one, no full decode): a chromosome isdiploidif it opens with the §95b diploid-telomere0x44(else provisionallystick), andmintedif it contains an interior §95a centromere0x58. Precedence: minted > diploid > stick — the interior centromere overwrites the opener-based provisional, so a real diploid PAIR (which carries a centromere) readsminted, matching the R-RBS-LM reference's centromere-first classify; a purediploidcap_kind is a diploid-telomere opener with no centromere. This is a NEW additive field with no format/version bump: a v12 head-onlymanifest.jsonhas no on-diskchromosomesarray, socap_kindis derived on read exactly as the rest of the chromosomes array is (a v12 genome from any prior rc catalogs fine). - (B)
genome_census(path)(Python) +srmech_genome_census(C) — the per-genome roll-up{path, n_chromosomes, types:{stick,minted,diploid}, chromosomes:[{label,type,leaf_count}], total_leaves, topology}. A thin roll-up over the catalog (the TYPE rides the ONE body scan).topologyis a STRUCTURAL, INTEGER read (no float/libm), classified in C so no host reimplements it (§97 lesson): any minted/diploid →"nuclear-like"(a eukaryotic nucleus); elsen>0andtotal_leaves <= 8*n→"organelle-like"(a small all-stick mitochondrion/chloroplast plasmid genome); elsen>0→"plasmid/prokaryote-like"; else"empty". - (C)
genome_registry(root)(Python) +srmech_genome_registry(C) — the cell/melange census{root, n_genomes, genomes:[<census per genome>]}, sorted by name. Scansrootfor genome dirs (a dir with BOTHturns.binandmanifest.json); the C path uses the PAL directory surface (srmech_plat_dir_*, no#ifdefin the genome TU), Python usesos/pathlib. This is the "cell": which genome is the NUCLEUS (minted/diploid) vs an ORGANELLE (a small stick plasmid genome).
Both new public callables are registered as genome-category ToolEntrys (describe()["tools"]["total"] 440 → 442), _tool_docs entries, and Rosetta ledger composes_c rows. C↔Python parity is proven by native-vs-pure tests for the census + registry trees and the catalog cap_kind. Additive symbols only — SRMECH_ABI_VERSION stays 5. JPL Power-of-Ten holds (every new C function ≤60 lines / ≥2 asserts / no goto/malloc; the topology is an integer compare). New tests: python/tests/test_genome_census_rc267.py + the extended C smoke c/test/test_srmech_genome.c.
[0.9.0rc266]¶
genome_append is now O(1) in RAM as well as time — fixes a corpus-scale memory leak (§97 / #1407). The native append path (genome_append_c) was O(1) on disk and in time, but its working arena grew O(body) per call and was never released — a 240k-body corpus encode was OOM-killed at 95 GB RSS after only 3361 bodies. Two mis-detections of the v12 head-only manifest (which, by ADR-0003, has no on-disk chromosomes/regions arrays) were the cause:
- The O(1) tail-extend was keyed on a
regionsarray being physically present in the manifest — absent in every v12 genome — so each append was mis-routed to the legacy whole-body-migration arena (body_hint += the whole turns.bin). The shared module arena (_genome_ws, grown-to-max and never shrunk) ballooned to a whole-body-rebuild size. Now keyed onformat_version >= 4(the region-chain era), so v12 takes the O(1) path and the arena stays manifest-sized. _genome_chrom_countread thechromosomesarray (absent in v12) →KeyError→ an O(n) whole-turns.binscan on every append. Now it reads the scalarn_chromosomesfrom the head (O(1)); the array is only the legacy fallback.
C↔Python parity — the classification now lives once, in C. The fix above corrected the Python wrapper's copy of the arena-sizing classification, but that classification was duplicated: the C op srmech_genome_append already classified v12 correctly (it checks "regions" or "n_chromosomes"), while the Python wrapper carried a second copy that had drifted. A bare-C host sizing its own append arena had no library function to call and would have to reimplement (and could re-drift) the same logic. So this rc adds a shared C source of truth, srmech_genome_append_arena_bytes(dir, region_len, ws, ws_len, *out_bytes), which reads the manifest and classifies by the same byte-substring probe the op uses, then returns the exact arena size; genome_append_c now calls it (the rc266 Python sizing is retained only as a stale-DLL fallback). Any host — Python or standalone C — sizes the append arena from this one place (ADR-0003). Additive symbol; does not bump SRMECH_ABI_VERSION (stays 3).
Sizing this in C also surfaced a second, smaller growth the Python-only fix would have kept: passing the full chromosome count to srmech_genome_arena_bytes makes the arena O(n_chromosomes) (its n_chroms · per_chrom term, ~2.7 KB/append). The v4/v12 tail-extend stages one region slot + a head-only 1-entry manifest — it never materialises the per-chromosome array — so the arena is sized with n_chroms = 1 and is now O(1) in the chromosome count too, not just the body.
A v12 genome written by any prior rc appends identically, now with bounded RAM. Verified: test_native_append_arena_is_o1_not_o_body + test_c_helper_sizes_append_arena (the raw C helper's arena does not track a 300 KB body growth over 200 appends) + the C smoke test_srmech_genome (append_arena_bytes is manifest-scaled with n_chroms=1, and an append bounded to exactly that size succeeds) + the JPL Power-of-Ten ratchet (the new C function is ≤60 lines / ≥2 asserts / no goto/malloc). PKG-3's corpus encode can drop the batch/explode/pack workaround and stream.
[0.9.0rc265]¶
genome_append streaming ergonomics — a discoverable resume path + a clear error for the catalog={} footgun (§95.2 / #1407). The O(1)-per-append machinery already existed (thread the returned catalog dict), but the resume-with-no-prior-return case was a trap: catalog={} raised a bare KeyError: 'leaf_dim', and nothing told a caller how to start a streaming loop against an existing genome on disk.
catalog="load"(new) — resume a streaming append with no prior return in hand: reads the full threadable catalog from disk ONCE (O(n)), does the append, and returns a dict to thread for the rest of the loop (O(1)/append). Byte-identical to threading a catalog derived up front.catalog={}/ a partial dict now raises a clearValueErrornaming the three modes (Nonecold / a threaded dict /"load"), never a bareKeyError.- Docstring now enumerates the three
catalog=modes with streaming + resume examples, and flags that looping with the default rebuilds the catalog each call (the O(n²) wall) — thread it.
Pure Python — the native disk-append path (genome_append_c, O(1) tail-extend + head write) is unchanged; no format / ABI change. Verified: catalog="load" resume is byte-identical to threaded (turns.bin + region chain), and catalog={} raises a clear ValueError (test_genome_o1_append_rc115).
[0.9.0rc264]¶
Diploid erasure repair is now SYMMETRIC — a break on EITHER homolog heals from the intact one (§95.4 / #1407, found by a reviewer using rc262). The rc259 changelog promised "exactly one ERASED → fill from the intact homolog" (direction-free), but a copyA erasure did not heal — the recovered leaf was the uncoupled garbage, not the truth. Root cause: recover_diploid tested the erasure sentinel on the decoupled leaf, but a break zeros the stored turn, and a zeroed turn decouples to a non-zero leaf — so the erasure was never actually detected. Healing only happened by substitution-tiebreak luck (which defaults to copyA), so a copyB break "worked" and a copyA break did not — asymmetric.
- Fix:
_diploid_ec_leaf(and its C peergenome_diploid_ec_leaf) now take the two stored turns and read the all-zero erasure sentinel before decoupling, then decouple the survivor. A break on either homolog now heals from the intact one, byte-exact. This also closes a latent false-erasure: a real leaf whose stored turn happened to equalthe_one(decoupling to all-zero) was wrongly "healed" before — now only a genuinely zeroed turn is an erasure. - No format / ABI change — the on-disk diploid format is unchanged (v14); only the recover logic changed. A genome written by any prior rc reads identically (and now heals correctly).
genome_diploid_ec_leafis a static C function (no header/ABI touch).
Verified: test_genome_diploid_rc259 now asserts erasure recovery on both homologs (the §95.4 regression) with the correct all-zero-turn sentinel; C↔Python byte-parity on the clean + erased strands; integrate (rc262) + centromere (rc258) + JPL + version parity green.
[0.9.0rc263]¶
The stdlib-fractions purge — srmech carries every exact rational in its OWN C-native Q carrier (#845). Self-hosting: a bare-C host with no Python stdlib runs the same exact-rational math. srmech is its own maths library; it already borrows nothing from stdlib math (rc13) or numpy (rc75–rc133). rc263 closes the last stdlib-math dependency — fractions.Fraction — routing every exact rational through srmech.amsc.q.Q (a reduced (num, den) pair whose reduce/multiply ride the native srmech_rational_* / srmech_bigint symbols). BREAKING (rc-stage, TestPyPI-first): ops that emitted a Fraction now emit a Q — value-identical (Q == Fraction, same reduced pair) and full-numeric-protocol compatible, but type(x) is Fraction identity checks change to Q.
srmech.amsc.q.to_q(v)— the single-argument drop-in forfractions.Fraction(v): coerces aQ/int/float(exact, viaQ.from_float) /(num, den)pair / anyas_integer_ratio-able carrier (a stdlibFraction, anotherQ) to an exactQ.Q↔Fractionnow interoperate in BOTH directions —Q._as_pairlearned thenumbers.Rationalprotocol, soQ * Fraction,Fraction * Q,Fraction + Q, and mixed comparisons/sum()all reduce exactly (a caller's stray stdlibFractionstill combines with a srmechQ).- Migrated (emit
Q, still ACCEPT aFractionon input): the Cayley–Dickson element carrier (cd_mult/cd_conjugate/cd_add/cd_norm_sq/cd_basis/cd_promote/cd_project/ the sedenion zero-divisor witness +left_mult_kernel), the exact-LAdense_solve(exact=True)/schur_complement/dirichlet_to_neumann,cycle_holonomy's exact holonomies, the Sturm / complex eigenvalue-isolation interval oracles,matrix_cascadesroot isolation, the octonion-DFT Q61 boundary, theso8rank +trialityGauss–Jordan solvers, the arithmetic coder,op_provenancecanon, and every_coerceaccept-branch across thePoly/Qalg/QMat/QPoly/TriPoly/EllMonomialcarriers. - The registered
"Fraction"interchange carrier was removed —Qsubsumed it (it is now THE exact-rational carrier across srmech). Thecycle_holonomy/recover_check*charge param type is nowint | Q | float; the MCP charge wire (_seq_charge/serialise_native) speaksQ ↔ [num, den].
Verified: test_fractions_to_q_rc263 (to_q↔Fraction parity, both-direction Q/Fraction arithmetic, migrated ops emit Q + accept Fraction) + a test_no_stdlib_fractions_import AST ratchet (zero fractions imports in srmech/ source, keeping it gone); the carrier-schema C-registry byte-parity, rosetta / tool-schema / tool-docs coverage, MCP-coercion, and carrier-ladder ripples all green (both C registries regenerated). A stdlib Fraction remains ACCEPTED everywhere on input via the numeric protocol — it is simply never the emitted carrier.
[0.9.0rc262]¶
The coherency-translation-layer capstone — integrate(): a stick provirus integrates into a minted/diploid genome on ONE shared k=3 coupling (§95.1d / #1407 / F1244). Completes the #1407 biology-native genome architecture arc. A Tier-1 stick provirus (a retrovirus genome — telomere-capped, no centromere) integrates INTO a Tier-2 host genome (a eukaryote — minted + diploid chromosomes), and thereafter every mode still recovers — because rc258 centromere, rc259 diploid, and the mint umbrella ALL couple every turn through the same the_one (one k=3 cascade at different rungs). The translation between the Tier-1 and Tier-2 levels is free: no conversion, because they are the same cascade.
integrate(host, provirus, *, at=None)— splices a provirus chromosome-strand into a host genome-strand at a chromosome boundary (at= the host chromosome index to insert before; default = after the last). Both must sharethe_one(the coherence contract). The coherence is demonstrated, not engineered: after integration,partitionrecovers every chromosome,centromere_ofstill reads the host's minted chromosome,recover_diploidstill recovers its diploid, and the provirus recovers too. Strand splicing (no re-coupling — the provirus's turns are already coupled); a C-only host integrates identically by concatenating the two genomes' self-describing chromosome regions.
Verified: test_genome_integrate_rc262 (integration + the full coherence proof: partition / centromere_of / recover_diploid all survive integration, the at= locus, provirus validation, and a disk round-trip); the integrate new-public-callable ripple (rosetta / tool-schema / tool-docs / tools.total count / both C registries) green; JPL + pedantic clean. Pure Python — integrate composes existing self-describing chromosome blocks (no C / format / ABI change).
The #1407 arc is complete: §95a centromere (rc258) · §95b diploid (rc259) · §95.2 rename (rc260) + config-aliasing (rc261) · §95.1d coherency-translation-layer (rc262). All three genome modes — stick, minted, diploid — are one k=3 cascade, and a stick integrates into a minted/diploid genome for free.
[0.9.0rc261]¶
Config-driven FUNCTION ALIASING — bind your own name to any srmech.* function via TOML (§95.2 / #1407). The domain-agnostic naming layer. srmech already lets a researcher declare CLASSES (make_class) and PIPELINES (the [chain] DSL) in TOML; rc261 adds the smallest missing rung — declaring a name binding — so the framework's own naming (e.g. the rc260 genome/plasmid rename) is a non-issue at the user layer: anyone re-aliases to their domain vocabulary in config, no code.
srmech.dsl.alias(name, target)— bindsnameto the srmech function at the dottedtargetpath (viafunctools.wraps, preserving its signature/docstring), carrying the user'sname.build_aliases_from_toml_str(spec)/load_aliases_toml(path)parse a[[alias]]TOML array (name+target) into a{name: callable}mapping; parsing reuses the DSL's native (srmech_toml) +tomllibloader.- Security (load-bearing): a
targetMUST be a dottedsrmech.*path — the naming layer binds names to srmech's OWN surface, never arbitrary imports (a config cannot be coaxed intoos.system/subprocess.run/ any non-srmech module — rejected withValueError). Config gives names, not capabilities.
[[alias]]
name = "build"
target = "srmech.amsc.genome.genome"
[[alias]]
name = "stick"
target = "srmech.amsc.genome.plasmid"
New governance: adr/0004-config-driven-domain-agnostic-surface.md codifies that srmech's user-facing surface is config-driven (classes, chains, catalogs, and now names in TOML) — the property that makes srmech domain-agnostic. Pure Python (no C / format / ABI change); test_dsl_function_alias_rc261 covers the single + TOML-batch aliasing, byte-identical target behaviour, and the srmech.*-restriction; the rosetta + non_compute four-way-split ratchets green.
[0.9.0rc260]¶
Genome API rename — genome() is now the biology-aware umbrella; plasmid() is the pure all-stick builder (§95.2 feedback 2, #1407). BREAKING for large kernels. "genome" is the umbrella noun, but it was the dumb all-stick builder while mint() was the smart one. rc260 fixes that:
genome(kernels, the_one)is now the biology-aware umbrella — it PICKS each chromosome's shape per kernel (the oldmint()behaviour): a plasmid-scale kernel (tome/mobius, ≤4 leaves) stays a Tier-1 stick, a eukaryotic-chromosome-scale kernel (quad_strand, ≥5 leaves) is minted with an interior centromere. The threshold isencode_shape's attested criterion (F715, no magic number).plasmid(kernels, the_one)— the pure all-stick builder (biology's plasmid: small, appendable, no centromere), the OLDgenome()behaviour, renamed.mint()— kept as the explicit alias ofgenome()(the "structured build" name; byte-identical).
Breaking scope is narrow: genome() only changes for ≥5-leaf kernels (they now get a centromere); kernels ≤4 leaves stay sticks exactly as before, so most existing genome() calls are byte-identical. Callers that specifically want the all-stick build should use plasmid(). No on-disk-format, C-symbol, or ABI change — plasmid() reuses the existing srmech_genome_genome C peer and genome()/mint() reuse srmech_genome_mint (a C host builds all-sticks via srmech_genome_genome); genome format stays v14.
Verified: the full genome suite passes pure + native (the single behavioural test updated to the new plasmid/genome/mint roles); the new-public-callable ripple for plasmid (rosetta / tool-schema / tool-docs / tools.total count / both C registries) is green; JPL + pedantic -Werror clean.
(The config-TOML function-aliasing layer from the same review — a [[alias]] descriptor binding a user's name to any srmech function — lands next as rc261.)
[0.9.0rc259]¶
Genome on-disk format v14 — the §95b DIPLOID pairing primitive (#1407 / F1244): the erasure/break specialist, the second rung of the biology-native genome architecture. A diploid chromosome (marker 0x44 'D') stores two homologous copies of a kernel (maternal | paternal) split by an interior rc258 centromere whose orientation is the which-template mark — 2 copies + 1 mark = 3 = the k=3 triality (F291). It composes the centromere directly: the centromere IS the diploid mark.
diploid(leaves, the_one, *, label="diploid", orientation=None, repeats=15)— builds[diploid_telomere, copyA…, centromere(orientation), copyB…](copyA == copyB). A deterministic content-addressed store writes identical homologs; the redundancy is read-time EC.recover_diploid(strand, the_one)splits at the centromere and error-corrects per leaf: both agree → use it; exactly one ERASED (an all-zero leaf — a detectable double-strand break) → fill from the intact homolog; both present but disagree (a substitution) → trust the centromere which-template mark. Measured (R-RBS-LM-DIPLOID-EC): on the erasure channel diploid reaches triality-level fidelity at 2× not 3× — biology's break-repair, the erasure specialist (triality is the substitution specialist; they meet in the k=3 coherency tower).recallstill returns the raw 2n turns (both copies);recover_diploidreturns the corrected n.- A chromosome-BOUNDARY cap like CHROM —
0x44opens a chromosome (carries a label inline), sopartition/genome_save/genome_loadrecognise a diploid in a mixed genome; the interior centromere is excluded from the leaf-count.> 3and distinct from every prior marker → v2..v13 bodies read UNCHANGED (dual-read, the walker gains ONE branch, no migration). - 1:1 C↔Python byte-parity (
SRMECH_GENOME_FORMAT_VERSION13 → 14):srmech_genome_diploid(builder) +srmech_genome_recover_diploid(the two-copy EC — a C-only host builds + recovers a diploid end-to-end) ship in lockstep; the cap-kind + all chromosome-boundary walkers (partition / count / scan / append-region) recognise0x44. Byte-parity verified: the diploid strand, the recover (clean + erasure), and a diploid genome'sturns.bin+manifest.jsonare all identical between C and pure Python. ABI unchanged (additive symbols).
Verified: the full genome suite passes native-present (225 — C-path + C↔Python byte-parity); test_genome_diploid_rc259 passes pure + native (the EC per-leaf, erasure recovery, partition + persistence, and the parity gates); the C library builds -Werror-pedantic and passes the JPL Power-of-Ten audit.
[0.9.0rc258]¶
Genome on-disk format v13 — the §95a CENTROMERE primitive + the MINT shape-selector (#1407 / F1243): the first rung of the biology-native genome architecture. A genome was haploid — one telomere-capped stick chromosome per kernel, genome_append-grown. rc258 adds biology's centromere: an interior anchor that carries a chromosome's GLOBAL orientation-chirality (the strand's handedness, distinct from Klein-4's local per-leaf chirality), and a mint constructor that lets the tooling pick each chromosome's shape by modeling biology rather than the caller dictating it.
-
The centromere cap (marker
0x58'X'— the cross-point of the X-shaped chromosome). A new self-describing INTERIOR cap (a byte > 3, distinct from every prior marker; v2..v12 bodies read UNCHANGED — the walker gains ONE branch, no migration). It sits BETWEEN a minted chromosome's two arms, so its POSITION in the strand IS the p:q arm-ratio (biology: the centromere position defines the arms). It stores the global 4-way orientation as biology's α-satellite REPEAT-ARRAY — R copies of the sector (default R=15) — majority-decoded on read (klein4_triality_correct's 2-of-3 generalised to R; a Class-K sector count + argmax, noabs/float/numpy). Measured (R-RBS-LM-CENTROMERE-CHIRALITY, F1243 §1): this recovers the global which-way at ~15× fewer bits than per-leaf Klein-4 (R=15 → 39 bits vs 600) with matching random-noise robustness — taking the GLOBAL which-way off Klein-4 so G4 stays for the LOCAL chirality that varies along the strand. New public surface:centromere(orientation, *, repeats=15, handle="cen", dim=64)+centromere_of(strand) → {orientation, arm_ratio, handle, repeats};chromosome(leaves, one, *, centromere=o[, centromere_at=split])mints one. -
mint(kernels, the_one)— the tooling picks the shape. Same signature + return asgenome(), but per kernel the attestedencode_shapecriterion (F715, keyed to 256 = 2⁸ + the Klein-4 order 4 — no magic number) routes a PLASMID-scale kernel (tome/mobius, ≤ 4 leaves) to a Tier-1 STICK (append-only, no centromere, byte-identical togenome()) and a EUKARYOTIC-CHROMOSOME-scale kernel (quad_strand, ≥ 5 leaves) to a Tier-2 MINTED chromosome with an interior centromere carrying its global orientation (the content-address folded to a sector,sha256(raw leaves)[0] & 3).genome()= all sticks (unchanged, back-compat);mint()= the tooling picks.mint_plan(kernels)returns the per-kernel picks (shape / tier / centromere / orientation / reason) — build nothing, watch it pick. -
1:1 C↔Python byte-parity (C-host-standalone, ADR-0003).
SRMECH_GENOME_FORMAT_VERSION12 → 13. The C peer ships in lockstep:srmech_genome_centromere(cap writer),srmech_genome_mint(the full selector + assembler — a C-only host mints a genome end-to-end, not just persists one built in Python), andsrmech_genome_centromere_of(the majority read).genome_cap_kind+ the body-scan recognise the interior cap (recall/partition flatten past it; it is excluded from the per-chromosome leaf-count; it is NOT a chromosome boundary). Byte-parity verified: the centromere cap, the mint strand,centromere_of, and a minted genome'sturns.bin+manifest.jsonare all identical between C and pure Python. ABI unchanged (additive symbols; the genome format version is a data-format version, distinct fromSRMECH_ABI_VERSION).
Verified: the full genome suite passes pure-Python and native-present (188 — C-path + C↔Python byte-parity); the dedicated test_genome_centromere_rc258.py passes 24 + 7-parity (native) / 24 (+7 skipped, pure); the C library builds -Werror-pedantic and passes the JPL Power-of-Ten audit (Rule 4 ≤60-line functions, Rule 5 ≥2 asserts/function). New governance: adr/0003-c-host-standalone-no-python-assumption.md codifies the C-host-only deliverable as standing policy.
[0.9.0rc257]¶
Genome on-disk format v12 — O(1) genome-native genome_append (fixes the O(N²) build-loop wall a downstream consumer hit) + no plaintext catalog sidecar. The genome body (turns.bin) was already O(1)-append (tail-extend + body_sha256 region-chain fold), but the manifest.json catalog was fully re-read, re-copied, re-serialized, and re-written on every append — O(n_chromosomes) per call → O(N²) over a build loop — and that per-chromosome chromosomes/regions array is a plaintext table-of-contents living outside the genome (a genome-native / no-plaintext-TOC design violation).
v12 makes the on-disk manifest HEAD-ONLY — {format_version, leaf_dim, n_turns, n_chromosomes, the_one, body_sha256 chain head} — and drops the chromosomes/regions arrays from disk. They are derived by scanning the self-describing body (the telomere caps carry the labels inline, so the catalog rebuilds byte-identically) whenever a full catalog is read. So genome_append rewrites only the tiny fixed-size head (O(1)) instead of the whole array.
genome_append(path, label, leaves, the_one, *, catalog=None)— writes only the DNA (tail-extend) + the O(1) head; drops the O(n) duplicate-label scan (labels are content-addresses → last-wins). Thread the returned dict back ascatalog=(data = genome_append(..., catalog=data), the natural build-loop shape) and the full-dict return stays O(1) (mutate-append in memory); a cold call derives the full catalog once.genome_save/genome_pack/genome_remove/genome_replaceall write head-only. A legacy v2/v3 genome (whole-body digest, not a chain) migrates on its first append.- Reads are unchanged for callers —
genome_catalog/genome_load/genome_windowreturn the full catalog (derived from the body for a v12 head-only manifest; a v≤11 manifest with the arrays reads verbatim). Integrity is preserved: a body corruption re-derives a differentbody_sha256chain ≠ the committed Merkle head →GenomeBoundingError. The body format is UNCHANGED, so v2..v11 genomes read identically. - Native C peer in lockstep (
SRMECH_GENOME_FORMAT_VERSION11 → 12):srmech_genome_appendtakes the O(1) head-append (read the head fields + scan only the new region + fold the chain + write the head — no per-chromosome array copy), the writers emit the head-only manifest, and every C reader derives the catalog from the body for a head-only manifest (via the sharedgenome_obtain_manifest). C↔Python byte-parity verified across every op (save / append / remove / replace / export / import / explode / pack).
Note (siona/consumers): reading the full catalog now scans the body (it is derived), where a v≤11 read did not — the genome-native tradeoff; the follow-on mmap catalog.idx restores an O(1) body-free open at corpus scale. ABI unchanged (stays 5) — the genome format version is a data-format version, distinct from SRMECH_ABI_VERSION; no exported-symbol signature changed.
Verified: the full genome suite passes both pure-Python (155) and native-present (188 — the C-path + C↔Python byte-parity); a deterministic O(1)-append regression guard (test_append_is_o1_head_only_fixed_manifest_v12 — the manifest stays fixed-size as chromosomes grow); measured per-append time FLAT (ratio 1.02× vs the old 6.68× O(N²); 800 small appends 0.60 s vs 8.4 s; manifest.json fixed ~1.2 KB head-only vs 223 KB growing).
[0.9.0rc256]¶
srmech_bigint_divmod_small — a single-limb-divisor bignum op — and the native ThetaSum.is_zero Z5/Z6 prime factoring moved onto the coefficient CARRIER, so the peer no longer value-declines a large-coefficient constant leaf. The rc228 Z5 / rc255 Z6 theta-constant-leaf certificates factor the leaf coefficients to find the primes to lift; they did this by DOWNCASTING each srmech_bigint coefficient to int64 (ti_bi_to_i64) and declining to the pure oracle when a coefficient exceeded int64 — a value-domain "fall back to Python" on part of the peer's input domain. rc256 factors the bigint carrier directly: the new srmech_bigint_divmod_small(q, uint32_t *rem, a, d) (Python-FLOOR quotient + uint32 remainder, one limb at a time, no ws and no per-divisor allocation — the fast peer of srmech_bigint_divmod) drives the trial-division and the prime factor-out, so an arbitrarily large coefficient MAGNITUDE is handled and the peer is C-standalone-complete (a leaf's coefficient can now be 2^70, 3^41, … and native still decides it, where the int64 path returned None). The trial-division is JPL-bounded at 2^16 (a leftover fitting uint32 after that sweep is provably prime, since any composite < 2^32 has a factor ≤ 2^16); an elliptic-identity coefficient is a product of the interpolation augment primes (≤ ~617), two orders below the bound, so no real leaf declines.
A benchmark decided against keeping the int64 path as a fast path. Both kernels were compiled behind a flag and timed end-to-end over a Z5/Z6 constant-leaf corpus (best-of-5, 30 000 native is_zero calls): int64 4463 µs/call vs bignum 4454 µs/call — identical (within noise). The factor + lift cost is negligible next to the ±-pair reduction (the same exact-ℚ bignum arithmetic in both paths), so the int64 downcast bought no speed while costing a decline and a second code path. The int64 baseline, the compile flag, the #if guards, and the dispatch wrappers were all removed — a single carrier-native path remains (ti_collect_leaf_primes / ti_lift_terms / ti_lift_mono).
ABI-additive: a new symbol (srmech_bigint_divmod_small), no wire-format change, so SRMECH_ABI_VERSION stays 5. Verified: the bigint C smoke (test_srmech_bigint, now wired into the pedantic CI gate — was an un-built dev smoke) covers divmod_small across signs and multi-limb dividends; a 23-object native-vs-pure parity sweep including three large-coefficient leaves (2^70, 2^64, 3^41-scaled seams) the int64 path used to decline — native now decides all of them, native == pure, Z6 zeros proven True, zero false zeros; the thetasum soundness / corpus-parity / Z5 / Z6 / soundness-battery suites all pass; pedantic build clean.
[0.9.0rc255]¶
The native ThetaSum.is_zero interpolation peer now DECIDES a top-level all-constant (0-variable) leaf directly — completing the native Z5/Z6 theta-constant-leaf certificates (closes #849, the rc254 follow-up). rc254 fixed the all-constant OOB flake by making the peer cleanly decline an n_syms == 0 numerator to the pure oracle (native had only the Z5 single-prime lift, not the pure Z6 multi-prime collapse re-grading — a Z6-zero would have false-negatived). rc255 mirrors Z6 into C so the peer decides the leaf itself: the entry reserves synthetic slots for an all-constant object — up to TI_Z6_MAX_SUBSET (3) fresh lift slots + one reserved p-power slot (the ±-pair reduction tracks the Rosengren Eq. 1.6 p-multiplier, the pure oracle's always-present _P), with xsym = ysym = -1 (a pure constant leaf has no canonical x/y) — so the recursion reaches the n_live == 0 leaf and runs Z5 then Z6. The new ti_z6_leaf is the 1:1 mirror of thetasum._z6_theta_constant_zero: it enumerates every subset S (size 2..3) of the leaf's first 12 distinct primes in itertools.combinations order, chain-lifts each subset simultaneously to its own slot (distinct primes commute, so r sequential single-prime lifts == the joint lift), and closes the joint lifted object by the exact Weierstrass ±-pair reduction — bounded by TI_Z6_MAX_ATTEMPTS (512, a JPL compiled cap: no interpolation, no recursion, cannot loop). A subset that closes to the empty normal form proves leaf = L(S := primes) ≡ 0 by specialization (a theorem, never a numeric band → Z6 emits ONLY zero proofs). SOUND + total: a leaf whose prime-lift coefficient exceeds int64 still declines (None) to the arbitrary-precision pure oracle; the parallel peer delegates the constant case to the sequential peer (byte-identical verdict). Net effect: the disjoint-seam rank-2/rank-3 Weierstrass identities (A + B, A + B + C) that rc254 deferred to pure are now proven True by native; verdicts are unchanged (pure already decided them correctly) — this is an accelerator completion, not a decision change. C + tests only; no ABI change (ABI stays 5; no new exported symbol, no wire-format change). Verified by a 20-object native-vs-pure parity sweep (every all-constant object: native == pure, Z6 zeros proven True, zero false zeros) plus the strengthened test_thetasum_z6_collapse_rc235::test_z6_native_equals_pure + test_all_constant_native_interp_no_stale_exps_rc254 (now asserts the single-seam leaf is proven True, not declined).
[0.9.0rc254]¶
Fixed an intermittent ThetaSum.is_zero native-vs-pure divergence on an ALL-CONSTANT (0-variable) numerator — a stale out-of-bounds read in the interpolation C peer. The srmech_thetasum_is_zero_interpolation peer's ti_parse strided the flat exponent buffer by the CLAMPED c->n_syms (which is 1 when the real n_syms is 0, since the entry clamps (n_syms == 0) ? 1 : n_syms to avoid a zero-size allocation), while the marshalled exponent rows carried the REAL n_syms = 0 columns — so after the first monomial every memcpy read PAST the end of the 1-element exps_flat. The garbage exponent made present[0] pick up a phantom variable, so the recursion took the interpolation branch instead of the constant-leaf Z5 certificate and blew the arena → a false SRMECH_ERR_OVERFLOW decline on Linux (harmless — the caller falls to the sound pure oracle) or an occasional wrong verdict on macOS (the failure mode that surfaced as an intermittent CI flake in test_all_constant_three_term_z5_certified_rc228). The fix is two-fold: (1) ti_parse now zeroes the clamped exponent row and strides / copies by the REAL in_n_syms (0 columns for an all-constant leaf → clean zero exponents, never an OOB read); and (2) the peer now cleanly declines an n_syms == 0 (all-constant) numerator at both entries — the documented "native declines all-constant (no lift slot)" contract — so the complete + sound pure Z6 oracle decides. Pre-fix that decline happened only by ACCIDENT (the OOB → false OVERFLOW), which occasionally yielded a wrong verdict on macOS instead; it is now deterministic. The pure oracle and the full is_zero soundness battery are unchanged (the native constant-leaf Z5/Z6 certificates were never actually reached at top level before — the OOB always tripped first — so this is the first time that path is exercised, and it correctly defers to pure). C-only; no ABI change (ABI stays 5). Regression guard: test_all_constant_native_interp_no_stale_exps_rc254.
[0.9.0rc253]¶
Documentation catch-up for the #1390 directed Class-L genome-storage op family (rc248–rc252). The CHANGELOG backfills the five op-family releases below, and the README public surface (laplacian row) lists the new ops. No behavior change — docs / packaging only; no new tool, no C symbol, ABI stays 5. The op families shipped in rc248–rc252 (all merged to main, all native==pure byte-exact where a C peer exists); this release makes the PyPI long-description reflect them.
[0.9.0rc252]¶
The octonion ORDER faculty — the additive 5th recover_check faculty (#1390 item 4b, F1231). laplacian.order_fingerprint(fiber_ids) is the path-ordered product of a generic octonion per node along a walk — an order-sensitive fingerprint (8 ints, independent of walk length) that CATCHES a graph-preserving reorder the op / operand / responsion / ℂ-curvature faculties are BLIND to (F1079 / F1230: two orders can share the identical directed graph, so even the ℂ magnetic Laplacian passes both). recover_check_order(true_fingerprint, recovered_fiber_ids) is the order-integrity guard. Composes the C-routed qm.so8.octonion_mult_table with a generic (non-basis, non-uniform-component) per-node octonion; exact integer products (no mod). A verifier (lossy by pigeonhole), never a store. composition_of_c; no new C symbol; ABI stays 5.
[0.9.0rc251]¶
laplacian.recover_check — the packaged round-trip integrity check of a stored directed Class-L graph, plus the corpus-scale split (#1390 item 4, F1225 / F1227). recover_check(vocab_size, edges, weights, charges=None) verifies the four faculties a genome must recover — op (L = D − A eigendecomposes, PSD, a ~0 mode), operand (weighted edges present, non-degenerate, uncapped), responsion (the propagator e^{−zL} is excitable), curvature (a directed store keeps its charge; a symmetric bag is flagged flat, F1210) — returning {ok, op, operand, responsion, curvature:{directed, n_cycles, holonomy_nonzero, verdict}, diagnostics}. ok == op and operand and responsion — curvature is reported honestly, NOT a hard gate. Integer charge is phase-scaled (q = 1/(2·max|c|+1)) so it does not alias to 0 mod 1. Because the dense op / responsion faculties need vocab_size ≤ 256, the F1227 split ships alongside: recover_check_structural (operand + a sampled-curvature read, O(edges), any scale) and recover_check_spectral (op / responsion on a bounded principal submatrix). A domain-free composition of shipped C-routed ops (composition_of_c; no new C symbol); ABI stays 5.
[0.9.0rc250]¶
laplacian.eulerian_path / eulerian_circuit — the Hierholzer walk-reconstruction the directed Class-L genome store recovers an ordered sequence with (#1390 item 3, F1224). A node-agnostic Eulerian trail / circuit over a DIRECTED edge multiset: the ordered node walk consuming every edge once, or None if no single Eulerian trail exists (feasibility is checked — degree balance + full-edge-consumption connectivity — never a partial walk). Deterministic (adjacency consumed from the end). Byte-identical C peer srmech_eulerian_walk (CSR Hierholzer, integer nodes; any-hashable nodes stay on the pure body). Additive symbol; ABI stays 5.
[0.9.0rc249]¶
genome.graph_to_kernel / kernel_to_graph — a domain-free codec that stores a directed signed graph as a content-addressed genome (#1390 item 2, F1222). Serialises a sparse SIGNED integer graph (vocab_size + edges + int weights[metric] + optional signed charges[direction] + optional node_ids label table + extras metadata) into a self-describing Klein-4 symbol stream for kernel_pack, and inverts it BYTE-EXACT: graph_to_kernel(...) -> (strand, n_syms); kernel_to_graph(chroms, the_one, n_syms) -> {vocab_size, edges, weights, charges, node_ids, extras}. Undirected (charges=None) / unlabeled (node_ids=None) / metadata-free (extras=()) all round-trip. Each int is base-4 digits behind a 2-symbol length header (≤ 15 digits = 30 bits; Class-K zig-zag sign on the charge). Byte-identical C peers srmech_graph_kernel_encode / _decode. Additive symbols; ABI stays 5.
[0.9.0rc248]¶
text.cooccurrence_edges(directed=True) — a metric + charge SUPERSET of the co-occurrence graph (#1390 item 1, F1226). directed=False is unchanged: (n, edges, weights). directed=True returns (n, edges, metric, charge) on the SAME canonical (i < j) edges, where metric == the undirected weights (w_fwd + w_bwd) and charge == w_fwd − w_bwd (the direction the unordered fold discards; reversing the corpus flips charge exactly). metric + charge feed magnetic_laplacian as weights + charges — the front of the directed Class-L genome pipeline. Byte-identical C peer srmech_text_cooccurrence_edges_directed (accumulates both columns on the canonical key in one pass). Additive symbol; ABI stays 5.
[0.9.0rc242]¶
A bare-C host can now OBSERVE srmech's own op dispatch — the everything-to-C completion of Class-H self-introspection (#840). Until now srmech's introspection stream (~/.srmech/run-*.ndjson) was written ONLY by the Python srmech.introspect.Writer at Python op boundaries; the C library itself emitted nothing, so a no-Python host could not see which op ran. rc242 adds a registered C callback — a host installs srmech_progress_cb_t cb with srmech_set_progress_cb(cb, user_data), and the central invoke spine (srmech_invoke_tool / _json → iv_dispatch) fires it once per dispatched tool with a compact canonical-JSON event. This is the C-side half of the deferred-from-v0.4.6 introspection extensions (the Tier-2 mmap ring buffer remains deferred — need-gated).
-
The event mirrors the Python introspect wire-format.
{"category": <category>, "mpr_version": "1.0", "op_name": <dotted name>}, built through thesrmech_jsoncanonical writer (the keystone) so it is byte-identical to CPythonjson.dumps({...}, sort_keys=True, ensure_ascii=False)— the SAME shape (and", "/": "separators)srmech.introspect._event.serializeemits. A host may append the line straight to its own NDJSON stream, enriching it with a timestamp / pid the way the PythonWriterdoes: the C library reports WHAT ran, not WHEN (the clock is the host's), keeping the emit a pure function of the dispatch — deterministic, byte-exact-testable, libm/time-free. -
Off by default; bounded; one emit per real materialisation. With no callback registered the emit path returns after a single NULL-pointer test (the hot dispatch path pays nothing). The emit is guarded on the real write pass (
buf != NULL), so a two-pass (size-then-write) caller observes exactly one event per dispatch. The JSON tree + write scratch are carved from a thread-local static arena (JPL Rule 3 — no malloc; reentrant across threads per the #772 TLS pattern). -
ABI 4 → 5. The new
srmech_progress_cb_tfunction-pointer typedef carries a CFUNCTYPE wire-format implication for the Python ctypes shim (the v2→v3 / v3→v4 callback-typedef precedent), so ABI bumps;srmech_set_progress_cbitself is an additive symbol. Verified end-to-end through the C invoke spine (tests/test_progress_cb_rc242.py— register a trampoline, dispatchcascade.net_chirality, assert the exact canonical event + off-by-default silence; the bare-C-host smokec/test/test_srmech_progress.cproves the same with no Python).
[0.9.0rc241]¶
The CARRIER (operand) introspection surface now carries a per-carrier CONSTRUCTION example (#839, the operand-side peer of rc240), and the F1216 Class-L-store / Class-M-working-memory role split is woven into the introspection so Siona (RBS-LM self-hosting) reads which carrier for which purpose straight off srmech. All 25 carriers gain an example (how to build/obtain the carrier): a REAL executed construction where one exists (15 carriers, e.g. Mat ← dense_laplacian(...), One ← the_one(...), HV ← klein4_bind(...), Q ← Q(3,4)), else an honest usage snippet for the domain-object / float-sequence carriers (never a fabricated result). composition_of_c (introspection metadata); NO new C symbol; ABI stays 4.
-
F1216 baked into the introspection. Per F1216 (Class-L Laplacian = the long-term relational STORE — exact, addressed, directional, GROWS with knowledge; Class-M Klein-4/HDC bundle = WORKING MEMORY / active context — fuzzy, composable, bounded, decays; the reversible spectral basis-change eigen/Walsh-Hadamard is the lossless bridge), the role split is now stated on the two anchor carriers (
Mat,HV) and the two anchor tool ops (laplacian.dense_laplacian,hdc.bind). A consumer introspecting srmech reads "use Class-L for the exact/permanent store, Class-M for the transient working context" directly, no external doc. -
C-mirrored via the fragment path (no struct change). The carrier registry bakes each entry as a pre-canonical compact-JSON fragment that the C serialiser splices, so adding the
examplekey flows throughsrmech_carrier_registry.c+ itssha256(C) == sha256(pure)attestation (test_carrier_schema_rc205.py) with only a table regeneration — nosrmech_carrier_entry_tfield, no serialiser edit. -
Reproducible + curation-preserving.
tools/gen_carrier_examples_probe.pyexecutes each carrier's canonical construction and writes the generated literalsrmech/amsc/_carrier_examples.py(CARRIER_EXAMPLES), merged intocarrier_schema()'s per-carrier payload. A coverage RATCHET (test_tool_docs_coverage_rc240.py::test_every_carrier_has_construction_example) pins the 100%-carrier-example floor.
[0.9.0rc240]¶
The introspection surface now carries a per-op EXPLANATION + EXAMPLE (#838) — every one of the 423 tools, not just the one-line summary. ToolEntry gains an explanation field (what the op does / when to use it) alongside the existing example, and BOTH are now populated for 100% of tools: the explanation is docstring-seeded, and the example is a REAL executed input→output where the op can be safely called (95 tools) or an honest signature usage-snippet otherwise (never a fabricated output). The ~17 most-central ops (the Class-L spectral family dense_laplacian/jacobi_eigvals/fiedler_vector/three_fold_eigvec_groups/spectral_spine, the_one, the HDC bind/bundle/similarity, the cascade atoms magnitude/pin_slot_at_zero/net_chirality, and the number cascades gcd/mod_pow/best_rational/is_prime) are hand-curated with high-quality framework-native prose + verified executed examples. Consumers: Siona (RBS-LM self-hosting) + MCP clients now see how to CALL each tool, not just its signature.
-
C-mirrored end-to-end (everything mirrors).
explanationis a real field on the Csrmech_tool_entry_tstruct, emitted by the registry codegen and by the canonical JSON serialiser (srmech_tool_schema.c, in its sorted-key slot betweenexampleandmcp_callable), so it flows through thetool_schema_sha256attestation exactly likesummary/example— a bare-C host produces the enriched registry.composition_of_c(docs metadata, not compute); NO new C symbol; NO ToolEntry add/remove (tools.totalstays 423); ABI stays 4. -
Generation is reproducible + curation-preserving.
tools/gen_tool_docs.pywrites the generated literalsrmech/amsc/_tool_docs.py(TOOL_DOCS), merging hand-curation fromsrmech/amsc/_tool_docs_curated.py(CURATED, probed bytools/gen_curated_probe.pyso every curated example is a genuinely executed result). The docs are merged into eachToolEntryatregister_tooltime (a hand-written literal on the registration always WINS). A new coverage RATCHET (tests/test_tool_docs_coverage_rc240.py) pins the 100%-explanation + 100%-example floor as a hard invariant + a monotone executed-example floor (curation grows it, never lowers it) + guards the silent-drop footgun (everyCURATEDkey must be a real registered tool name). -
Scope note. This ships the OPERATOR (tool) side, the primary "per-op example and explanation" ask. The CARRIER (operand) side already carries rich human-readable descriptions from the #1293
carrier_schemawork (name + what-it-is + variable semantics + op back-index — that IS the carrier "explanation"); per-carrier construction examples are a lower-value follow-up. -
Verification. gcc-13.4
-O2 -DNDEBUG -Wall -Wextra -Wpedantic -Werrorcompiles the enriched (non-NULLexplanation) C table + serialiser CLEAN; codegen idempotence + registry count green; the coverage ratchet +test_tool_registry_c_rc184+test_mcpb_emit+test_llm_anthropicpass (41 passed, 7 native-only skipped in the numpy-absent dev tree).
[0.9.0rc239]¶
The bus pub/sub SERVER now runs on WINDOWS (#801) — the last platform gap in "everything mirrors" — plus a PyPI-README refresh (the winding w on the One; the bare-C-host tooling). rc180 landed the pub/sub server in C but SKIPPED its 3 driving tests on Windows because the named-pipe transport hung there; rc239 fixes the transport (C-only change in c/src/srmech_platform.c, the Windows PAL) and un-skips them. composition_of_c, NO new C symbol, NO new ToolEntry (tools.total stays 423), ABI stays 4 (the added stop-event slot is C-internal caller-storage, not the ctypes wire format).
-
The two Windows-only bugs, and the fix. (1) Accept-race: the Windows PAL created its listening named-pipe instance LAZILY inside
accept(POSIXlistenpre-binds), so a client whoseCreateFileraced ahead gotERROR_FILE_NOT_FOUND, gave up, and left the server blocked forever in a synchronousConnectNamedPipewaiting for a client that would never come. (2) Teardown wake: a synchronousConnectNamedPipeis NOT cancelled by aCloseHandlefrom another thread, soserver_closecould not deterministically wake a blocked accept (POSIX gets this free fromclose(listen_fd)). Fix (the primary option named in #801 / rc180):srmech_plat_stream_listenpre-creates the first instance (mirrors POSIX pre-bind → closes the race) + creates a per-server manual-reset stop-event;srmech_plat_stream_acceptuses an overlappedConnectNamedPipewaited viaWaitForMultipleObjects({connect, stop})and pre-arms the next instance (endpoint name never vanishes between accepts → a mid-sequence client sees the RETRYABLEERROR_PIPE_BUSY, notERROR_FILE_NOT_FOUND);srmech_plat_stream_server_closeSetEvents the stop-event (the POSIXclose(listen_fd)analog). A uniform overlapped-capable read/write helper serves BOTH the overlapped server handle AND the synchronous client handle.srmech_plat_stream_connectalso gets a boundedERROR_FILE_NOT_FOUND/ERROR_PIPE_BUSYretry. -
Verification. Built + tested on NATIVE WINDOWS (MSVC 2022): the 3 previously-
@posix_only-skipped pub/sub tests (test_bus_pubsub_c_rc180.py, now un-skipped on Windows) + a new req/rep round-trip guard (test_bus_server_windows_rc239.py, the regression guard for the overlapped read/write) pass 10/10 with a hard 30s per-test timeout (the pre-fix build reproduced the hang at exactly the accept). POSIX unaffected: gcc-13.4-Wall -Wextra -Wpedantic -Werrorbuilds clean in BOTH-O2 -DNDEBUGand asserts-live-O1(the Windows branch is#ifdef'd out on POSIX; the shared struct just gains an unused field); MSVC/W4 /WXpedantic wheel build clean. JPL ratchet green (each new function ≤60 lines, ≥2 asserts, no goto/malloc; bounded loops). -
PyPI-README refresh (docs only). Fixed the "the One as a matrix" description to carry the winding triad
w = (w_saros, w_metonic, w_callippic)(rc137) —the_one(σ, θ, w):θthe epicycle half-angle,wthe metacycle grade (the spinor double-cover sign(−1)^Σw+ the divmod binary tower) that the flatto_matrix()epicycle realisation alone does not carry. Added the bare-C-host "everything mirrors" note (the orchestration —srmech.busreq/rep and pub/sub, thesrmech.dslchain interpreter, the MCP server over stdio + HTTP/SSE with in-C tool dispatch, the CLI,make_class— all inlibsrmech, so a host with no Python runs the whole apparatus, plus the exact-ℚ algebra tail onsrmech_bigint), surfaced the new Class-L relational read-outs (fiedler_vector/three_fold_eigvec_groups/spectral_spine/relational_structure/magnetic_laplacian/signed_laplacian/recursive_cut), and freshened the stale illustrativenative_status()output (ABI 3 → 4,0.8.0→0.9.0). 5 SSOT files rc238 → rc239. -
Two PRE-EXISTING main-red regressions FIXED (folded in so rc239 ships onto a fully-green main; both unrelated to the Windows fix, both from earlier rcs — user-directed 2026-07-13). (1)
coupling.pyfloat_pow (from rc230/#698): the numeric k-extreme eigensolver's L2-norm (_kext_normalize) and 1/√n constant-mode normaliser wrote bare-Pythonx ** 0.5, which the down-onlytest_an_cascade_ratchet(CEIL_FLOAT_POW=0) correctly flagged — routed both throughrational.sqrt(float(_rsqrt(...)), the Class-N∘K integer-isqrt cascadelaplacian._fsqrtalready uses; within-tol, the coupling / resonant-spectrum parity suite stays green). (2) the Anthropic tool-name overflow (from rc232/#829):_to_anthropic_namemapped…riemann_theta_multisum.multivariate_riemann_theta_sum(65 chars) 1:1.→_, one past Anthropic's 64-char grammar ceiling — made the mapper ROBUST to any valid srmech name by deterministically shortening an overlong swap to a readable prefix +_+ an 8-hexsha256_bytestag of the full name (round-trip preserved: the reverse map is rebuilt by the SAME synthesis; the srmech canonical name + the generated C registry are untouched — no public rename). Both verified locally (the 2 previously-failing test files + the numeric-parity suite green).
[0.9.0rc238]¶
NEW EXACT carrier — the FRAME-CARRYING CARRIER: augment a 2π-periodic truncated-Taylor value with its local beat-frame (σ, w) so a cross-seam compare PARALLEL-TRANSPORTS the frame first → cross-seam BIT-EXACTNESS holds where the un-framed compare was only SOMETIMES exact (#1385 F-thread; user directive "an exact-rational carrier / Taylor series across a beat seam must carry its local frame-rotation; cross-seam compare must parallel-transport the frame first"). Realizes the live thread op / operand / responsion ≅ field / excitation / CURVATURE + bit-exactness = the local FLATNESS of a connection; seams expose HOLONOMY ([[user_stance_bit_exact_is_local_flatness_of_connection_seams_are_holonomy]]) — the code realization of the connection/curvature idea rc236 (separate_frame_curvature) / rc237 (separate_winding_curvature) established at the operator/schema level, now on a VALUE carrier. TWO new public ops (tools.total 421 → 423); EXACT + SOUND (no numpy, no float shell, no abs()); composition_of_c, NO new C symbol (ABI stays 4).
-
The drift this FIXES (why it is NOT a re-wrap of
winding_fold/the_one— the #830 shell rule). The periodic Class-N series carrierssin_series_truncate/cos_series_truncatetruncate the RAW rational argumentp/qdirectly — they do NOT fold it (their own docstring: "caller should reduce to [-π, π]"). So a truncatedsinatθand atθ + 2πgives two DIFFERENT exact rationals (the truncation is about the EXPANSION POINT; the raw series drifts and eventually BLOWS UP past the convergence radius), even thoughsinis 2π-periodic — "exact WITHIN a beat (|x|≤π, windingw=0) but NOT bit-exact ACROSS the 2π seam" under today's carrier.winding_foldcannot fix it (it returns a float residue re-quantised to a2⁻⁴⁴grid — it cannot feed the EXACT-rational series the exact residue the fix needs);the_onecannot fix it (its 2π-periodic adjoint FOLDS the winding away — it never carries a truncated-Taylor VALUE across a seam). The delta here: augmenting the sin/cos series carrier — which currently LOSES the frame across the seam — with(σ, w, residue)+ a transported compare that makes it bit-exact. -
The
(value, frame)contract — the rc125 recoverable-fold(lossy_bundle, exact_seed_R)analogue with the FRAME as the exact second leg.frame_carrier(func, numerator, denominator, num_terms, sigma=1)(srmech.amsc.cascade.frame_carrier) returns{func, arg, sigma, winding, residue, num_terms, value, transported}:value= the LOSSY leg (the raw local seriesσ·series(arg, N), exact in-beat, DRIFTS across the seam);winding/residue/sigma= the EXACT frame (the connection element / metacycle windingwthe raw carrier loses);residue= the canonical in-beat representative (|r| ≤ π,θtransported tow=0);transported=σ·series(residue, N), the seam-INVARIANT value the compare aligns on. The transport is an EXACT-RATIONAL 2π divmod over the SAME Machin-2π anchor_EPH_TWO_PI(Class-I quotient-retained divmod over the exact Class-N 2π; residue sign Class K/C, neverabs()) — NOTwinding_fold's float residue, NOT_eph_seam_fold's grid re-quantise. -
The transported cross-seam compare + the exact
is_alignedcertificate (rc236'sis_flat/ Stab shape one level up).frame_carrier_compare(func, num_a, den_a, num_b, den_b, num_terms, sigma_a=1, sigma_b=1)returns{func, raw_equal, transported_equal, aligned, transport_turns, transport_magnitude, residue_delta, chirality_match}:raw_equal= the UN-framed compareA.value==B.value(the "sometimes not bit-exact across the seam" DRIFT symptom — comparing two carriers in DIFFERENT local frames without transporting);transported_equal= the FRAMED compareA.transported==B.transported(each argument exact-rational-folded to its canonical residue FIRST — bit-exact across the seam where the raw compare only sometimes matched — the win).alignedisTrueiff the transport lands in the value's winding-STABILIZER — the canonical residues are EQUAL (a whole number of 2π turns apart, a zeroresidue_deltaby its Class-Kcascade.magnitude) AND the chiralities match;aligned is Trueis then a THEOREM (the transported values are byte-identical). SOUND (no false-alignment): when NOT aligned it carries the real residual — a non-zeroresidue_deltaholonomy (e.g. ≈πfor asin(θ)vssin(θ+π)=−sin(θ)half-turn) orchirality_match=False(oppositeσ) — so a genuinely DIFFERENT value is never falsely reported aligned. (cos even-symmetrycos(r)==cos(−r)is handled CONSERVATIVELY —transported_equalTrue butalignedFalse, sincer ≠ −ris not a 2π turn — never a false alignment of unequal values.) -
Classification
composition_of_c, NO new C symbol (ABI stays 4). The values ride the c_dispatched Class-Nsin/cos_series_truncate(srmech_{sin,cos}_series_truncate_big); the transport composes the exact Machin-2π anchor_EPH_TWO_PI+ the round-half-up_eph_round_div(Class-I quotient divmod, Python-bignum glue reaching no non-standalone leaf — the SAME shape asseparate_winding_curvature's holonomy sum); the reduction rides the c_dispatched Class-Irational._reduce_rational(srmech_bigint/cyclic.gcd); the alignment residual + the transport count ride the c_dispatched Class-Kcascade.magnitude(real|x|, neverabs()). Native == pure byte-identical by construction (every leaf is a byte-exact bignum/integer op). Both ops JSON-native (int/str params, dict return) → registered ToolEntries; Rosetta rowscomposition_of_c(transitive-standalone reachable — reaches only c_dispatched leaves;CEIL_PYTHON_ONLY_DEBT/CEIL_BIGNUM_REFERENCE/CEIL_NON_COMPUTE_OWEDall stay 0). -
Registration + collateral. New
srmech/amsc/cascade/frame_carrier.py(__all__ = {frame_carrier, frame_carrier_compare}); 2 newToolEntry→tools.total421 → 423 (the Csrmech_tool_registry.cREGENERATED, itstool_schema_sha256byte-ratchet re-locks; the carrier back-indexsrmech_carrier_registry.cREGENERATED to match; 51 pinnedtools.total == 421count-asserts across 45 files swept to 423). No new carrier CLASS (returns a dict record, likeseparate_frame_curvature) → no carrier-registry class row, no coverage-exempt entry needed (registered under the full canonical name). Build gcc-13.4-O2 -DNDEBUGAND asserts-live-O1, ZERO warnings under-Wall -Wextra -Wpedantic, no assert fires; JPL ratchet green (no hand-written C). Newtests/test_frame_carrier_rc238.py(the seam-drift-fixed case + is_aligned + no-false-alignment soundness + within-a-beat + value-oracle + registration). 5 SSOT files rc237 → rc238.
[0.9.0rc237]¶
The two DEFERRED FACES of rc236's separate_frame_curvature — F2 the the_one(σ,θ,w) WINDING instance + F3 the responsion_schema CURVATURE property — completing the op / operand / responsion ≅ field / excitation / CURVATURE thread on the metacycle seam and at the schema level. Both are the schema/carrier lift of rc236's exact is_flat; both are EXACT + SOUND (no numpy, no float shell, no abs()); neither adds an MCP ToolEntry (tools.total stays 421) or bumps the ABI (stays 4).
-
F2 —
separate_winding_curvature(one)(NEW exact op;srmech.amsc.cascade.one, alsoOne.separate_winding_curvature()). Thethe_onewinding instance of the connection/curvature split:fixed_frame= the w-INVARIANT adjoint (the 14 exact rationals ofOne.to_flat_rational, byte-identical for every winding — the unwoundS(σ,θ,0)representative, the frame the 2π-periodic base folds the winding away in) ⊕curvature= the winding-HOLONOMY (the full ℤ³ grading BEYOND the ±1spinor_sign: the winding triad + its divmod binarywinding_tower), with the EXACTis_flatcertificate (True ⇔ every winding component vanishes by its Class-Kcascade.magnitude⇔w == (0,0,0)). NOT aspinor_signshell: the ±1 double-cover parity CONFLATESw=2withw=4(both Σ even → +1), but the curvature record DISTINGUISHES them (towers(0,1)vs(0,0,1)) and calls BOTH curved —is_flatis full-period-trivial, not merely even-parity (aw=(2,0,0)hasspinor_sign+1 yet is NOT flat). Classificationcomposition_of_c(composes the c_dispatchedsrmech_the_oneadjoint +srmech_winding_tower+srmech_spinor_sign+ Class-Kcascade.magnitude; no new C symbol). Reachable via Python / theOnemethod, not the MCP tool list (structuredOnearg + carrier return — coverage-exempt liketo_scalar/separate_frame_curvature). -
F3 — the
responsion_schemaCURVATURE property (tool_schema : carrier_schema : responsion_schema :: connection : sections : CURVATURE). Each responsion now carries a"curvature": "flat" | "curved"field — the schema lift of rc236'sis_flat, classifying whether the op reads frame-INDEPENDENTLY on that carrier (flat) or CAN carry a frame-dependent holonomy (curved). SOUND:"flat"ONLY on the two airtight certificates — a COMMUTATIVE carrier ([A,B] ≡ 0by the ring axiom:Poly/BiPoly/TriPoly/QPoly/QBiPoly/EllRatio/EllMonomial) or akind == "trace"read (Tr[A,B] ≡ 0by cyclicity — theheat_traceinvariant) — and conservatively"curved"everywhere else (never a FALSE flat). Result: 17 flat / 8 curved; the SOLE flatMatedge isheat_trace(the trace invariant);the_one|Oneis CURVED — the very winding holonomy F2 decomposes (the two faces agree). Derived (never authored) from(carrier, kind); the Csrmech_responsion_registry.cconst table is regenerated so the bare-C-host canonical JSON stays BYTE-IDENTICAL to the Python SSoT (the sha256 hash-ratchet re-locks; the field rides inside each edge's pre-canonicalentry_jsonfragment — no struct change, ABI 4).
[0.9.0rc236]¶
NEW EXACT op separate_frame_curvature(A, B) — the connection/curvature decomposition applied as an operator: split a two-operator product A·B into its FIXED-FRAME (metric) part ⊕ its CURVATURE / RESPONSION (holonomy) residue, with an EXACT is_flat vanishing certificate (#834; user directive "rearrange those equations to separate the curvature from the FIXED FRAME"). Realizes the live framework thread op / operand / responsion ≅ field / excitation / CURVATURE and bit-exactness = the local FLATNESS of a connection; seams expose HOLONOMY ([[user_stance_bit_exact_is_local_flatness_of_connection_seams_are_holonomy]]). The same collapse ⊕ residue shape Z6 (rc235) instantiates on the theta leaf and the the_one winding grading instantiates on the metacycle seam — here on an operator product.
-
The decomposition (the NEW object — NOT a bare
commutatorre-export). For two square operatorsA, B(Mator nested sequence), the product splits EXACTLY intofixed_frame = ½(A·B + B·A)(the SYMMETRIC part = the ANTICOMMUTATOR{A,B}halved = the METRIC / frame-aligned piece — what both orderings AGREE on, transported frame-INDEPENDENTLY) ⊕curvature = ½(A·B − B·A)(the ANTISYMMETRIC part = the COMMUTATOR[A,B]halved = the HOLONOMY / responsion residue = the geometric-phase wedge). This IS the Clifford / geometric-algebra product split (a product = its symmetric metric part ⊕ its antisymmetric wedge/curvature part; Lounesto Clifford Algebras and Spinors §2). The ANTICOMMUTATOR / symmetric half existed NOWHERE in srmech before this rc (onlysrmech.qm.single_particle.commutator = A·B − B·Adid) — the DECOMPOSITION plus the exact vanishing flag is the delta, not the wedge alone. Returns the small record{"fixed_frame": Mat, "curvature": Mat, "is_flat": bool}. -
is_flat— the EXACT (byte-sound) flatness certificate.is_flatis True iff the curvature is LITERALLY the zero carrier: every stored double of the curvatureMathas Class-K magnitude (cascade.magnitude, real|x|— never an ALUabs(), and — UNLIKE a squared Frobenius norm — with NO underflow-to-zero hazard) exactly0.0. Sois_flat is Trueis a THEOREM about the computed curvature carrier. On the exactly-float-representable entry regime — integer / half-integer / dyadic / Gaussian-integer matrices, i.e. the quantum-operator regime this is FOR (Pauli σ, Dirac γ, integer Hamiltonians, the Klein-4 sector operators) — the c_dispatchedmat_matmulis bit-exact, so the computed curvature IS the true½[A,B],is_flatis the TRUE flatness (curvature exactly zero ⇔[A,B]=0⇔ the pairing is FLAT / frame-independent / always bit-exact), andfixed_frame + curvaturereconstructsA·Bbyte-for-byte. (This is exactly the regime wheresingle_particle.commutatoris itself byte-exact native==pure; a genuinely-irrational float pairing is already in a rounded frame — its exact "do these commute" question is only well-posed on the exact carrier.) Proven:[σx, σz]non-commuting → curvature nonzero +is_flat False; two commuting diagonal / shared-eigenbasis operators → curvature the EXACT zeroMat+is_flat True;{σx, σx} = 2·Ianticommutator;fixed_frame + curvature == A·Bbyte-exact. -
Classification:
composition_of_c, NO new C symbol (ABI stays 4) — the SAME standalone-C shape assingle_particle.commutator. The two products ride the c_dispatchedmat_matmul(srmech_dense_matmul_complex); the symmetric/antisymmetric assembly + the½scale ride the c_dispatchedMat+/−/*carrier ops (srmech_mat_{add,sub,scale}); the flatness certificate rides the c_dispatched Class-Kcascade.magnitude.commutatoritself has no dedicated C kernel (it iscomposition_of_covermat_matmul+mat_sub, rc147), so a dedicatedanticommutatorC kernel would be asymmetric + gain nothing — the honest classification iscomposition_of_c(zero new C symbols, bare-C-runnable). The½is the Class-N symmetric/antisymmetric-projector normalization, never a magic number. -
Package-level: returns a dict carrying two
Matcarriers — a structured / carrier return that cannot cross the JSON-RPC boundary, exactly likeeig_exact/jordan_form_exactreturn dicts carryingQalg/complex. So it is a public Python op, NOT an MCP ToolEntry →tools.totalstays 421; added to the tool-schema-coverage exempt list + the Rosettacomposition_of_cledger (transitive-standalone reachable — reaches only c_dispatched leaves). ABI stays 4. numpy-free, noabs(), MIT. 5-SSOT0.9.0rc235 → 0.9.0rc236. Tests:test_separate_frame_curvature_rc236.py.
Honest deferred faces (#834 was a 3-face design; 1 ships, 2 deferred with rationale). (2) The the_one winding instance (fixed = the w=0 unwound representative ⊕ curvature = the winding-holonomy residue) is DEFERRED: on the current S(σ,θ,w) the winding enters ONLY through the Z/2 double-cover readout One.spinor_sign = (−1)^Σw / sigma_effective (a ±1 holonomy that is ALREADY exposed) — wrapping it would re-present a shipped readout (shell-risk, #830). (3) The responsion_schema curvature property (a flat-vs-curved edge per (op, operand-carrier) pairing) is DEFERRED: responsion_schema (rc225) is a pure-data registry hash-ratcheted to a compiled-in C const table (srmech_responsion_registry), so a new edge needs a C-registry regen + hash update + a serializable ToolEntry to reference — a heavier arc than this rc's exact-op core.
[0.9.0rc235]¶
NEW SOUND ZERO certificate Z6 for the ThetaSum.is_zero high-kernel-rank (rank-≥2) theta-constant leaf — the multi-prime COLLAPSE / re-arrangement certificate (ships as 0.9.0rc235; #833). Converts a class of "generally-open" 0-variable leaves into "closed" — the residual-after-collapse-search. The 0-variable theta-CONSTANT leaf Σ cᵢ ∏ θ(rational; p) (all summation variables consumed by the Z4 interpolation) is NOT generally undecidable — it is SOMETIMES open: its VALUE is genuinely zero, but the certificate FRAME cannot SEE the collapse until the right RE-ARRANGEMENT / GRADING aligns the "seam" (the same shape as the_one S(σ,θ), right value but not correct across the metacycle seams until the winding grading w was added in rc137). Z5 (rc228) lifts ONE prime back to an elliptic variable and closes the leaf iff a SINGLE summation seam suffices; a leaf carrying TWO OR MORE INDEPENDENT seams (a genuine high-kernel-rank sum) declines to is_zero = False. Z6 is that missing rung.
-
The certificate (SOUND —
Trueonly on a THEOREM, never a false zero). Z6 searches a BOUNDED family of value-preserving re-gradings: lift a SUBSETS(size 2..3) of the leaf's DISTINCT primes SIMULTANEOUSLY, each to its own fresh elliptic variable, LEAVING the un-lifted primes as the CONSTANT partner pairs the three-term rewrite needs. The lift is EXACT (substituting each variable := its prime reproduces the leaf identically), so the liftedLsatisfiesL(S := primes) = leaf; if the EXACT Weierstrass ±-pair reduction over the whole lifted variable set (_pair_reduce_component, Rosengren arXiv:1608.06161v3 Eq. 1.12, MPM-verified in-tree — a value-faithful rewrite provingL ≡ 0as a function of the lift variables) closesLto the empty normal form, thenleaf = L(S := primes) ≡ 0by SPECIALIZATION (a specialization of an identically-zero elliptic function is zero — a THEOREM, not a numeric band). Z6 produces ONLY ZERO verdicts, NEVER a NONZERO claim; a genuinely-nonzero leaf hasL ≢ 0so the sound ±-pair reduction never reaches the empty form. The subset re-grading IS the "winding" seam-alignment: freeing exactly the primes carrying the independent seams (one per seam) collapses each seam's ±-pair three-term structure simultaneously, where any single-variable slice (Z5) sees at most one. -
Coverage (a REAL rank-≥2 family the current system DECLINES). Z6 closes the INDEPENDENT-SEAM rank-≥2 leaf — e.g. the SUM of two three-term Weierstrass identities over DISJOINT prime alphabets,
three_term(2,5,7,x=3) + three_term(11,13,17,x=19): genuinely zero, butis_zerowasFalsebefore (Z5's single-prime lift frees only one seam, leaving the other seam's terms as surviving constants). Z6 closes it by lifting one prime from each seam (here [7,19]); the rank-3 analogue (three disjoint blocks) closes via a size-3 subset ([7,19,37]). SOUNDNESS proven by the perturbed siblings —A+B+1(extra constant),A_broken + B(one coefficient scaled), and the independent exact-Fractionp-expansion oracle (rc234 pattern:oracle nonzero ⇒ is_zero MUST be False) — all stayis_zero = False, Z6 declining each. -
The (3,3) residue — HONEST answer: Z6 does NOT close it (it is the remaining residual). The rc227 Aₙ (3,3) residual leaf (3 terms × 29 thetas over primes {2,5,29,71}, exponent lattice ℤ²⁹→ℤ⁴ with 25-dim kernels) is NOT an independent-seam sum: no bounded subset re-grading exposes a ±-pair collapse, so Z6 DECLINES it (fast, empirically confirmed), and it stays the correct
is_zero = False. The #695/#833 wall is NOT closed by Z6; closing it needs the NEXT rung of the theta-relation generating hierarchy — Riemann's QUARTIC (4-term) relations (Chai, "Riemann's theta formula", 2014;theta-relations_v2.pdfsha2562bc44f47169c6f9f8828a534d0a3555797db09397089121d61f69e07f0744396) / the systematic n-fold generalization (Sogo, "Theory of general theta relations, addition formulas, and theta constants identities", arXiv:2412.06076v1, 2024, the n=3 case worked in detail; PDF sha25691fc5cc6b2c8d74d51d9bba3935e626c89287f4de7fdd9190e1b6a31a9c47186) — a future Z7, NOT a widening of Z6. Both citations MPM-verified against the actual sources. -
BOUNDED (JPL-style, cannot loop / cannot hang). Only ≤
_Z6_MAX_ATTEMPTS(512) bounded ±-pair reductions over the first_Z6_MAX_PRIMES(12) primes, subset size ≤_Z6_MAX_SUBSET(3); NO interpolation, NO re-lift, NO_decide_structrecursion. Z6 runs only at a multi-term 0-variable constant leaf AFTER Z5 declines; the (3,3)-shaped hard leaf (4 primes) declines in ms, so the honest residue is never a performance cliff. Exact over the theta algebra — no float in the certificate, noabs()(Class-K sign), nomath/numpy. -
New
tests/test_thetasum_z6_collapse_rc235.py— the rank-2/rank-3 disjoint-seam family Z6 CLOSES (with the_z5DECLINE /_z6CLOSE contrast proving it is new coverage), the perturbed-sibling DECLINES (soundness), the exact-Fractionoracle cross-check over the corpus, the (3,3)-residue honest-decline result, and native == pure parity on the Z6 corpus (the constant leaves route to the pure oracle — the native peer has no lift slot at n_syms = 0). -
Ledger. Pure-Python over the EXISTING exact-ℚ carriers (prime-lift
_lift_prime_terms+ ±-pair reduction_pair_reduce_component) — NO new C compute kernel (the Cti_z5_leafsingle-prime lift is the sound subset of Z6; a top-level constant leaf routes to the pure oracle, so native == pure holds). Composition_of_c classification — no owed-C row (no new primitive).tools.totalunchanged (421),SRMECH_ABI_VERSIONstays 4. Build gcc-13.4-O2 -DNDEBUGAND asserts-live-O1, zero warnings under-Wall -Wextra, no assert fires; JPL ratchet green (C untouched). 5 SSOT files rc234 → rc235.
[0.9.0rc234]¶
SOUNDNESS-HARDENING of ThetaSum.is_zero — an ENLARGED rank-2 / cross-character adversarial battery (ships as 0.9.0rc234; #832). No soundness gap found; the battery is the deliverable (a permanent ratchet). The rc210 rebuild made is_zero a SOUND-TRUE-ONLY certificate architecture (True ⟺ Z1 exact-cancellation / Z2 Weierstrass ±-pair reduction / Z3s character-split / Z4 per-character interpolation at D+1 nodes / Z5 rc228 theta-constant prime-lift PROVES the numerator identically zero; False = "not proven"). The soundness-critical direction — is_zero must NEVER return True for a genuinely NONZERO ThetaSum — was stress-tested here on the HARDEST regime (rank-≥2 kernels and cross-character cancellations, the #823/#771 residue-2 corners the shipped battery did not cover). The hunt found NO false zero: the certificate architecture stays sound across every probed corner. This is a test-hardening rc (NO behavior change, NO C change).
- New
tests/test_thetasum_soundness_battery_rc234.py— a large, systematic adversarial battery EXTENDING (not duplicating) the shippedtest_thetasum_{is_zero_sound_rc210,degree_bound_soundness_693,iszero_corpus_parity_rc210,z5_theta_constant_rc228}. Regimes now covered: - rank-≥2 exponent kernels — a widened multi-family sweep (single-
θ(t·xᵉ)for e=⅔, ±-pairθ(t·x^±), 2-/3-/4-theta products witha·b(·c) const, two-variableθ(t·x)θ(t·y)) searched for exact low-pnullspace kernels (genuinely-NONZERO deep-cancellation objects, each with a PROVEN survivingp-valuation); every kernel found staysis_zero = False(dispatched AND pure). The committed rc210 rank-2 generator searched ONE family and found no kernel; this sweep is the enlargement. - cross-character sign cancellations — sums whose terms carry pairwise-distinct quasi-periodicity characters
μ_v(asserted distinct via_term_char_v/_joint_char), including an e=2 divided-difference analogue of the shipped e=3 witness B; none certified zero. - the interpolation-cap boundary — θ-multiplied three-term cores that raise the Z4 degree
D(odd theta count → the fast ±-pair path breaks, forcing per-character interpolation at exactlyD+1nodes) stay PROVEN zero, while a degree-matched perturbed sibling stays False. - mixed p-power / q-power characters (#694 key-hygiene) —
σ_x/σ_y-shifted identities (q/ppowers riding the arguments) stay proven; perturbed siblings stay False. - a 300-object randomised FUZZ over all of the above shapes — the hunt: every oracle-nonzero object has
is_zero = False. -
native == pure parity extended to the adversarial corpus (the rc210 corpus-parity contract).
-
The independent ORACLE (computational-provenance; never routes through the machinery under test). A finite sum of modified-theta products
N = Σ c·∏ θ(z;p)is a genuine Laurent series inp(and the kept variablesx,y):N ≡ 0⟺ EVERY series coefficient vanishes. The battery's oracle is the EXACTp-expansion in stdlibFraction(deliberately NOT srmech'sQ) with the parameter symbols specialised to distinct rationals (specialisation is a homomorphism, so a nonzero coefficient PROVES the un-specialised function nonzero) — a DEFINITIVE one-sided nonzero-detector:oracle sees a nonzero coefficient ⇒ is_zero MUST be False. For the deep-cancellation kernels the exact-Fractionkernel GENERATOR itself is the oracle (it proves the surviving valuation, no truncation). Noabs()/math/numpy; the oracle'sFractionis a TEST oracle, not the certificate (the certificate path stays exact-Q, float-free). -
Honest residue (NOT a gap). The genuine rank-≥2 linear-independence wall (a 0-variable high-kernel-rank theta-constant leaf that no in-budget ±-pair lift closes — Rosengren Prop 1.6.1 / the rc227 Aₙ (3,3) residue) stays a correct
is_zero = FalseDECLINE: the honest incompleteness boundary, exactly per the sound-True-only contract, not a soundness bug. -
Ledger. Test-only: NO C change, NO public op →
tools.totalunchanged (421),SRMECH_ABI_VERSIONstays 4. Build gcc-13.4-O2 -DNDEBUGAND asserts-live-O1, zero warnings under-Wall -Wextra, no assert fires; JPL ratchet green (untouched). 5 SSOT files rc233 → rc234.
[0.9.0rc233]¶
FIX of a LATENT ζ₈⁴ sign defect in the riemann_theta.transform κ 8th-root multiplier at genus g=⅔/4 (ships as 0.9.0rc233; #824; found by review R-1). RiemannTheta{,G3,G4}.transform returns the transformed theta-characteristic together with the ζ₈ exponent k of the theta-constant modular multiplier κ(γ)=ζ₈^k. The exponent was wrong for EVERY γ — and no shipped test caught it (LATENT): the shipped Göpel/Rosenhain g2–g4 suites test SINGLE transforms and no operational consumer composed two, so no shipped result was affected.
-
The bug (two independent parts). (1)
_kappa_exp8carried the Igusa characteristic phase φ_m in its REAL-characteristic coefficients(−4,+8,−16,−8)·(B·Dᵀ)while feeding the DOUBLED integer characteristicε'=2m', ε=2m''. That unabsorbed factor of 2 made terms ⅔/4 ≡ 0 (mod 8) and collapsed term 1 to{0,4}, so the multiplier was pinned to{ζ₈⁰,ζ₈⁴}={+1,−1}for every γ and could NEVER emitζ₈^{1,2,3,5,6,7}. (2)transformreturns the mod-2-REDUCED target characteristic, but the reduction signθ[ε'+2a; ε+2b]=(−1)^{p·b}·θ[p; q](a ζ₈⁴) was not folded intok, so the composed-vs-direct exponent defect was characteristic-DEPENDENT (transform(γ₂γ₁)≠transform(γ₂)∘transform(γ₁)). -
The fix (exact, Class-K). (1) The corrected integer Igusa phase
8·φ_m = −ε'ᵀ(DᵀB)ε' + 2·ε'ᵀ(BᵀC)ε − εᵀ(AᵀC)ε + 2·diag(A·Bᵀ)·(Dε'−Cε)— coefficients(−1,+2,−1,+2)withDᵀB(not the wrongB·Dᵀ). (2) fold the mod-2 output-reduction sign4·Σ(ε̂'ᵢ mod 2)·⌊ε̂ᵢ/2⌋intokso the returned(reduced characteristic, k)COMPOSES. Concretely: the translationΩ↦Ω+diag(1,0,…)onε'=(1,0,…)multiplies θ by exactlyζ₈¹=e^{iπ/4}(a genus-independent lattice computation), where the old code returnedk=4(−1). No float /abs()/math/ numpy; the sign is the Class-K pin-slot; exact integer mod-8 throughout. -
MPM. Classical theta-constant transformation law (Igusa, Theta Functions, Springer Grundlehren 194, 1972, Ch. V; characteristic map DLMF §21.5.9). The EXACT integer coefficients + the reduction fold are pinned by an OWN re-runnable high-precision numeric theta oracle (primary computational attestation), verified g1..g4 across ALL generators/characteristics (
kranging over the full{0..7}):docs/srmech/rbs_lm_research/theta_transform_multiplier_oracle_rc233.py. Literature anchor arXiv:0801.2543 (Cacciatori–Dalla Piazza–van Geemen 2008, verifying a genus-3 D'Hoker–Phong chiral-measure proposal) sha256e6edd3b217d138c20ff9e126e9801bb020042000736b0415d98297b164311797. -
1:1 C peer (same-rc). The identical defect was mirrored in
srmech_riemann_theta_{sp4,g3_sp6,g4_sp8}_char(rt{,3,4}_eight_phiphase + the public functions' output fold); fixed byte-for-byte and re-verified native==pure. Two now-unused block helpers (rt3_pqt,rt4_pqt) removed. Integer-only sign fix →SRMECH_ABI_VERSIONstays 4; no ToolEntry added →tools.totalunchanged (421). -
Regression. New
tests/test_riemann_theta_transform_sign_rc233.py(numeric-oracle ground truth + not-collapsed-to-{±1} guard + exhaustive independent-formula sweep + pure-path check, g2/g3/g4) FAILS on the old code and PASSES on the fix. The pre-installed R-1 strict-xfailtest_kexp_composes_consistently_all_generanow PASSES (marker removed — the close signal). Three bug-enshriningtest_kappa_translation_valuesassertions (rc73/rc77/g4_modular_rc85, which assertedk∈{0,4}) corrected to the true{0,1}. Build gcc-13.4-O2 -DNDEBUGAND asserts-live-O1, zero warnings under-Wall -Wextra -Wpedantic, no assert fires, ASAN/UBSAN clean; JPL ratchet green. 5 SSOT files rc232 → rc233.
[0.9.0rc232]¶
The HIGHER-GENUS theta-multisum reduction row — the genus-axis lift of the elliptic Aₙ/Cₙ Jackson rows (ships as 0.9.0rc232; #829). The multivariable elliptic reduction row (type-C Cₙ, rc90–98; type-A Aₙ, rc227) is graded by a single genus-1 torus (θ(x; p)). rc232 lifts the reduction ONE RUNG UP THE GENUS AXIS — a multiparameter summation formula whose summand is built from the genus-g Riemann theta function on a compact Riemann surface of arbitrary genus (g ≥ 2 is the genuinely-new regime; g = 1 degenerates to Warnaar's elliptic formula). This is the "higher-genus theta multisum" follow-on the user named when choosing the Aₙ row.
-
MPM scope (Phase 0). The recorded prior-scope hypothesis (
arXiv:math/0408366) was VERIFIED against the actual arXiv source — and its ATTRIBUTION was CORRECTED: math/0408366 is V. P. Spiridonov, "A multiparameter summation formula for Riemann theta functions", Contemp. Math. 417 (2006), 345–353 (NOT Rosengren, as the hypothesis recorded). Its Theorem (Eq. sum) is a genuine attested closed-form multisum for genus-godd Riemann theta functions; the referee's remark reveals it as the exact telescoping identityΣ_k (x_k−y_k)∏_{j<k}x_j∏_{j>k}y_j = ∏x − ∏ywhose per-summand ingredient is the genus-gFay trisecant identity (Eq. Fay; J. Fay, LNM 353, 1973), then = 0base case. Source PDF sha2568478af7407d26d0b0504d381cbe3c32a00f950c3b0c6ab8001a023b7e0c4c319(extracted arXiv e-printjhlm-fin.texread in full). -
New module
srmech.amsc.riemann_theta_multisum— the row, following the Aₙ rc227 pattern EXACTLY: ThetaBracket/ThetaBracketSum— the genus-godd-theta CARRIER (the operand vocabulary; peers ofTheta/ThetaSumon the genus axis, but ODD:[-u] = -[u], a pure Class-K±1antisymmetry, no monomial prefactor). The additive genus-gargumentuis carried MULTIPLICATIVELY by anEllMonomial(z_k + v(a,b)↔ the monomialZ_k·P_a⁻¹·P_b, so+↔·and−u↔.inv()); a bracket PRODUCT is a signed multiset of canonical arguments andThetaBracketSumis their free commutative ℤ-algebra — the additive carrier the identity's telescoping cancellation lives in.riemann_theta_multisum_lhs(z, points)— the LEFT-hand side (then+1-term multisumΣ_k L_k·∏_{j<k}g_j·∏_{j>k}h_j) as an exactThetaBracketSum.multivariate_riemann_theta_sum(z, points, *, verify=False)— the closed-form RIGHT-hand side∏ g_k − ∏ h_k; withverify=Trueit PROVES the reduction per call and returns{closed_form, verified}.- The per-call PROOF
(LHS − RHS).is_zero: the Fay identity rewrites each summand's leadingL_k → g_k − h_k(the ONE attested genus-ginput), after whichLHS − RHStelescopes to EXACTLY the emptyThetaBracketSum(a ring identity; free-monomial cancellation) — verified True throughn = 5, and a wrong/perturbed closed form is caught (→ False). - The base ORACLE —
_telescoping_rational_oracleproves the telescoping SKELETON exactly in ℚ (assign arbitrary distinct rationals tog_k, h_k, setL_k = g_k − h_k, checkΣ = ∏g − ∏h), the exact-rational analogue of the elliptic rows'p = 0oracle. -
Exact over the theta-bracket algebra: no float, no
abs()(the odd-theta antisymmetry sign is the Class-K pin-slot), nomath/numpy. -
1:1 C peer
srmech_riemann_theta_multisum(same-rc). A self-contained int32/int64 kernel (the coeffs are±1and the genus-godd-theta arguments are small integer exponent rows — NOsrmech_bigint/ellbase dependency): it builds the SAME bracket-product monomials for both the LHS multisum (side 0) and the RHS closed form (side 1), byte-for-byte, and the Python side folds them into theThetaBracketSum(trusted only after==the pure carrier, the complete alternative + parity oracle). Additive symbol →SRMECH_ABI_VERSIONstays 4. Malloc-free (builds in the output buffer in place), JPL-clean.
Ledger: +2 ToolEntries (multivariate_riemann_theta_sum + riemann_theta_multisum_lhs) → tools.total 419 → 421; +2 Rosetta rows (both c_dispatched); ThetaBracket/ThetaBracketSum are CARRIERS (no ToolEntry, exactly like ThetaSum/EllRatio). CEIL_* ceilings all HOLD. Build gcc-13.4 -O2 -DNDEBUG AND asserts-live -O1, zero warnings under -Wall -Wextra (-Wpedantic clean on the new file), no assert fires, ASAN/UBSAN clean on the new C peer; JPL ratchet green (new C functions ≤ 60-line, ≥ 2 asserts, no goto/malloc, NULL-before-assert, Class-K sign). 5 SSOT files rc231 → rc232.
[0.9.0rc231]¶
MCP completeness — the recently-added ops become genuinely MCP-invocable + a cascade-atom invoke-dispatch batch (ships as 0.9.0rc231; #810). Two test_mcp.py ratchets had silently accreted red since rc227/rc229 (they are outside the publish CI gate): the rc228+ ops declared parameter type-strings with NO coercion handler, and the every-tool invocation smoke tripped an isolation quirk. rc231 greens both and widens the C invoke spine.
- PART 1 — the type-coercibility + every-tool ratchets are green. Added the missing inbound coercers in
srmech.mcp._coercion._PARAM_COERCERSso every advertised param type is JSON-coercible AND the op is genuinely MCP-invocable (round-trips throughinvoke_tool): Optional[list[int | tuple[int, int]]](laplacian.klein4_gain_laplacian/klein4_relational_structuregains) — a per-edge V₄ gain rides as a JSON int0..3or a[g0, g1]bit pair (each pair re-tupled); the int-form and pair-form invocations are byte-identical (gains=[3]==gains=[[1,1]]).Optional[list[int | Fraction | float]](laplacian.cycle_holonomycharges) — a per-edge charge in turns rides as a JSON int / float / an exact[num, den]pair (rebuilt toFraction). Paired with an OUTBOUNDserialise_nativeFraction -> [num, den]branch so thelist[Fraction]holonomies round-trip exactly ([[1,4]] <-> [Fraction(1,4)]) instead of degrading to areprstring.list[EllMonomial](elliptic_jackson_an.multivariate_elliptic_jackson_an/an_vwp_multisum_lhsz/avectors) — got the PREFERRED EllMonomial coercer (not an allow-list): the existing_to_ellmonomialgained the GENERAL exact JSON form{"coeff": <int | [num, den]>, "exponents": {sym: exp}}(so a bare host can round-trip an arbitraryc·∏ sym^emonomial, everything-mirrors) alongside the symbol-name-string shorthand the Aₙ variables use. The Aₙ ops now invoke through MCP and return a genuineEllRatio/ThetaSum.- The every-tool invocation smoke (
test_every_advertised_tool_invocable) failedModuleNotFoundError: No module named 'conftest'when run standalone (pytest tests/test_mcp.py):tests/is a package, so pytest's prepend import-mode putspython/— nottests/— onsys.path, and the barefrom conftest import return_type_agreesonly resolved because an earlier-collected sibling (test_immolation) happened to inserttests/first. Root-caused:test_mcp.pynow carries its OWNsys.pathbootstrap (the same guardtest_immolation.pyalready has), so it passes standalone AND in the full suite.pytest tests/test_mcp.py→ 74 passed, 0 failed. - C-marshal parity: the three new param shapes stay Python-side (their host ops are not in the C invoke vtable — they defer to the pure path, inform-don't-limit; the
srmech_mcp_marshal_argmirror is agreement-based, not exhaustive, so a deferred tool needs no C marshal peer). - PART 2 — cascade-atom invoke-dispatch batch (#810
srmech_invoke_toolwidening, 35 → 38 c_dispatched tools in C). Three scalar Class-K / Class-C atoms now RUN in C through the invoke spine, each thunk mirroring its op's OWN native-dispatch boundary EXACTLY so the dispatched result is byte-identical to the pureserialise_result(invoke_tool(...))and every off-boundary shape defers: cascade.magnitude(x: float) -> float(ridessrmech_cascade_magnitude_f64; a JSON int defers — the pure op returns an int magnitude with a different repr).cascade.pin_slot_at_zero(x: float) -> (int, float)(ridessrmech_cascade_pin_slot_at_zero_f64; serialises as a 2-list[orientation, magnitude]).cascade.net_chirality(orientations: [int]) -> int(ridessrmech_cascade_net_chirality_i8; a bool / out-of-int8 element defers to the pure sign-normalising path).- The thunks CALL existing rc-earlier kernels — no new C symbol / typedef →
SRMECH_ABI_VERSIONstays 4; this is a composes_c widening (invoke_toolwas discharged at rc188, so NO ledger move). Parity prooftests/test_invoke_tool_cascade_atoms_c_rc231.py(19 cases incl. a 200-iter random float + int8-list sweep) asserts native == pure byte-for-byte, and asserts the off-boundary cases return(False, None). - Honest residue (PART 2 remainder — deferred, inform-don't-limit). The remaining c_dispatched tools stay pure-deferred for real reasons: the
rational.*transcendentals return exact rationals whose numerators exceed int64 (bignum result carrier not ready); thecascade.*hypercomplex / exact-LA carriers (cd_*,matrix_cascades.*,*_dft,the_one) are not bucket-(a) scalar/bytes shapes; thehdc.klein4_*family rides theHVbyte carrier the marshal deliberately defers (rc190 note); and the other scalar atoms (reorient,chiral_flip) carry a value-type polymorphism a single thunk cannot serve byte-identically without further shape work. The invoke vtable is at 38/240 c_dispatched tools; the rest defer to the complete pure path.
Ledger: NO ToolEntry / op count change (tools.total stays 419) — coercers + invoke thunks add no ops. No Rosetta move (the three atoms were already c_dispatched; invoke widening is composes_c). CEIL_* ceilings all HOLD. Build gcc-13.4 -O2 -DNDEBUG AND asserts-live -O1, zero warnings under -Wall -Wextra, no assert fires, ASAN/UBSAN clean on the new thunks; JPL ratchet green (the three new C thunks are ≤ 60-line, ≥ 2 asserts, no goto/malloc, NULL-before-assert, Class-K sign). 5 SSOT files rc230 → rc231.
[0.9.0rc230]¶
The F172 storage signature at UNBOUNDED n — resonant_spectrum_sparse (streaming k-extreme resonant read) (ships as 0.9.0rc230; #698). resonant_spectrum reads the storage signature (eigen-tensions + the Class-N/J resonance lock/libration verdict) only through the DENSE Class-L eigensolve, which the native kernels cap at MAX_NATIVE_NODES = 256 (dense O(n²) RAM, O(n³) eig). rc230 ships the streaming / out-of-core read of the SAME signature at unbounded n.
coupling.resonant_spectrum_sparse(edges_or_path, weights=, k=, n=, max_iters=, max_den=). Reads the k EXTREME modes — the k lowest-tension + k highest-tension eigenpairs of the COMBINATORIAL LaplacianL = D − W— via streaming power iteration + Gram-Schmidt deflation on the packed edge stream. Bottom-k ride the shiftσI − L(σ = 2·max_deg + 1, a Gershgorinλ_maxbound); top-k rideL; each new mode deflates against every found mode (so bottom/top never collide, and 2k ≥ n yields the full spectrum). RAM O(k·n), time O(k·|E|·iters), n UNBOUNDED — it breaks the n ≤ 256 dense wall the wayfiedler_sparsebreaks it for the 2-way cut. Composes the §51/§52 out-of-core machinery (write_packed_graph+ thefiedler_sparsestreaming matvec, extended from ONE mode to bottom-k + top-k). The k tensions then feed the SAME_resonances_from_tensionslock/libration readresonant_spectrumuses (Class-Nbest_rational+ Class-J prime-coordinate factor; smooth-den LOCK vs large-prime-den libration) — factored out and REUSED, not reinvented, so the verdicts are IDENTICAL to the dense read on the same tensions. Returns{"tensions": Vec (ascending), "modes": Mat (columns = eigenvectors), "resonances": [...], "k", "n", "n_modes"}(noforce_orders— a dense Lᵏ is not materialisable at unbounded n).- Same-rc 1:1 C peer
srmech_laplacian_k_extreme_modes_file(+srmech_laplacian_k_extreme_modes_arena_bytes) — the bottom-k + top-k power iteration + deflation run in C, streaming the packed 16-byte-record edge file via the PAL (the same formatfiedler_sparse_filereads), caller-arena, no node cap. Reuses thefiedler_file_scanstreaming-matvec machinery. Dispatches whenHAS_NATIVE; the pure-Python streaming read is the complete alternative — native == pure within float tolerance (same iteration, same arithmetic order). Additive C symbols →SRMECH_ABI_VERSIONstays 4. - The multiset-agreement proof (
tests/test_resonant_spectrum_sparse_rc230.py): on graphs with n ≤ 256 whose extreme modes are SEPARATED from the bulk (the F928from_bodiesJupiter+Galilean graph, a weighted path, a well-separated random graph) the k extreme tensions fromresonant_spectrum_sparseMATCH the denseresonant_spectrumeigensolve to ~1e-9 AND the Class-N/J lock/libration verdicts + exactbest_rationalratios are IDENTICAL; native == pure differentially. An n > 256 graph (past the dense native-eig wall) RUNS and its well-separated top-k match dense to ~1e-10, with native == pure. Honest envelope (documented): near-degenerate boundary clusters (a dense low-frequency bulk) converge slowly — the genuine limitation of ANY iterative extreme-eigenvalue read vs. a full dense eigensolve; the extreme modes a k-extreme read targets are the SEPARATED ones.
Ledger: 1 new public Class-L coupling op registered — ToolEntry ×1 (tools.total 418 → 419, C tool-registry regenerated + tool_schema_sha256 re-locked), Rosetta row ×1 (resonant_spectrum_sparse c_dispatched), coupling.__all__; the ~42 tool-count tests bumped 418 → 419. Fold-in fix (#698 pre-existing red): the rc227 Aₙ ToolEntries elliptic_jackson_an.{multivariate_elliptic_jackson_an, an_vwp_multisum_lhs} carried category="elliptic_jackson" (module is elliptic_jackson_an) → test_tool_schema_coverage.py::test_tool_schema_categories_match_module_structure was RED; corrected to category="elliptic_jackson_an" (the Cₙ elliptic_jackson.multivariate_elliptic_jackson is untouched — its module IS elliptic_jackson). CEIL_PYTHON_ONLY_DEBT / CEIL_BIGNUM_REFERENCE / CEIL_C_EXISTS_UNBOUND / CEIL_NON_COMPUTE_OWED all HOLD at their floors. Build gcc-13.4 -O2 -DNDEBUG AND asserts-live -O1, zero warnings under -Wall -Wextra, no assert fires; JPL ratchet green (the new C functions are ≤ 60-line, ≥ 2 asserts, no goto/malloc, NULL-before-assert, Class-K sign). 5 SSOT files rc229 → rc230.
[0.9.0rc229]¶
The fuller asymmetric-halves lattice handle — the V₄-gain (Klein-4-sector) Laplacian (EVEN channel) + cycle_holonomy (ODD channel) (ships as 0.9.0rc229; #687). srmech's chirality/asymmetry lattice handle was magnetic_laplacian — a one-axis, chirality-EVEN U(1) projection: flipping ALL chirality conjugates the matrix entrywise, and Hermitian spectra are conjugation-invariant, so NO eigenvalue read carries the which-way sign (the provable F552 "diagnostic, not predictive" ceiling). rc229 ships the fuller object as its two genuine channels.
laplacian.klein4_gain_laplacian(n, edges, weights=, gains=)— the EVEN channel. Each edge carries a V₄ = ℤ₂×ℤ₂ gain (TWO sign bits;gains[e]an int0..3or a 2-tuple(g0, g1);None→ identity). V₄'s FOUR real charactersχ_ab(g) = (−1)^(a·g0+b·g1)decompose the object into FOUR real signed LaplaciansL_χ = D̄ − χ(g_e)·A— the two-bit generalization of exactly howsigned_laplacianis the ℤ₂ (one-bit) instance. The two gain bits are handled symmetrically (no bit privileged — the phase-vs-beat semantic binding is reserved for the user). The signed degreeD̄ = Σ|A_ij|is the Class-K magnitude (noabs()) and is character-independent, soχ00(trivial) ==dense_laplacianfor unit gains, and the four sectors drop straight intospectral_block_dispatch. Returns{"chi00","chi01","chi10","chi11"} → Mat.laplacian.klein4_relational_structure(...)— the joint read-out. Per-sector spectral tension (λ_min = frustration, 0 iff balanced), coherence (λ₂), and the Class-K sector-asymmetry meter between the two MIXED sectors χ10/χ01 — the (4:3)|(3:4) sector-occupancy diagnostic (F552). Composition ofklein4_gain_laplacian+symmetric_eigendecompose; no dedicated C symbol.laplacian.cycle_holonomy(edges, charges=)— the ODD channel the spectrum provably cannot carry. A gain graph is determined up to switching by its cycle gains (Zaslavsky). Builds a spanning forest (union-find; first-encountered edge = tree edge) → the fundamental cycle per co-tree edge → that cycle's NET charge (per-edge charges in TURNS, exactFraction, reduced mod 1) — Class I (mod-1 cyclic) ∘ Class L (graph); NO eigensolve. Invariant under node re-gauging (a coboundary telescopes);balancediff every holonomy is 0 (Zaslavsky's balance criterion); distinguishes+cfrom−c(1/4vs3/4mod 1) — the chirality the sector spectra cannot. Returns{"n_cycles", "holonomies", "cycle_edges", "balanced"}.- Same-rc 1:1 C peers (
srmech_graph_klein4_gain_laplacianbuilds all four sectors in one call;srmech_graph_cycle_holonomy+srmech_graph_cycle_holonomy_arena_bytescompute the exact int64-rational cycle sums caller-arena, no malloc). Both dispatch whenHAS_NATIVE; the pure-Python cascades (foursigned_laplacianbuilds; the exact-Fractionholonomy) are the complete alternatives — native == pure (byte-identical for klein4; exact-equalFractionfor holonomy within the int64 charge range, the arbitrary-denominator pure path beyond it). Additive C symbols →SRMECH_ABI_VERSIONstays 4. - Six genuine theorem checks (not smoke tests) —
tests/test_klein4_gain_laplacian_rc229.py: (1) χ00 ==dense_laplacianexactly; (2) eachL_χ==signed_laplacianon χ-transformed weights; (3) the COVER-SPECTRUM THEOREM — the four sector spectra's multiset union equals the ordinary Laplacian spectrum of the explicit V₄ cover (4n nodes) exactly (the Bilu–Linial 2-lift generalized to the V₄ abelian cover); (4) all four sector spectra invariant under arbitrary V₄ node re-gauging; (5)magnetic_laplacian(charges ∈ {0,1/2})reproduces the corresponding sector (the U(1) projection of the V₄ object); (6)cycle_holonomyinvariant under switching, == 0 iff balanced, nonzero on a genuine odd cycle-gain AND distinguishing the ± chirality the (identical) magnetic spectra provably cannot. - Honest boundary (documented, F552): a Laplacian whose eigenvalues carry the which-way sign is provably impossible; the composite reads the ASYMMETRY (sectors differ; holonomy nonzero), and the ORIENTATION LABEL requires an external frame anchor —
cycle_holonomyis the gauge-invariant ODD datum (which-way relative to a chosen base gauge, not absolute). Attested SSoT: Reff, Spectral Properties of Complex Unit Gain Graphs, LAA 436 (2012), arXiv:1110.4554; Bilu–Linial, Combinatorica 26 (2006), arXiv:math/0312022; Zaslavsky, Signed graphs, DAM 4 (1982) 47–74; Lieb–Loss, Fluxes, Laplacians, and Kasteleyn's Theorem, Duke 71 (1993), arXiv:cond-mat/9209031 (also added tomagnetic_laplacian's attested docstring, filling the citation gap it flagged).
Ledger: 3 new public Class-L ops registered — ToolEntry ×3 (tools.total 415 → 418, C tool-registry regenerated), Rosetta rows ×3 (klein4_gain_laplacian / cycle_holonomy c_dispatched, klein4_relational_structure composition_of_c), laplacian.__all__ + LAPLACIAN_OPS; the ~42 tool-count tests bumped 415 → 418. CEIL_PYTHON_ONLY_DEBT / CEIL_BIGNUM_REFERENCE / CEIL_C_EXISTS_UNBOUND / CEIL_NON_COMPUTE_OWED all HOLD at their floors. Build gcc-13.4 -O2 -DNDEBUG AND asserts-live -O1, zero warnings under -Wall -Wextra, no assert fires; JPL ratchet green (the new C functions are ≤ 60-line, ≥ 2 asserts, no goto/malloc, NULL-before-assert, Class-K sign). 5 SSOT files rc228 → rc229.
[0.9.0rc228]¶
The Z5 theta-constant-leaf PRIME-LIFT ZERO certificate — ThetaSum.is_zero gains a sound zero certificate for the 0-variable theta-constant leaf that was the root of the #695 completeness wall (ships as 0.9.0rc228). The rc227 Aₙ (3, 3) post-ship diagnosis root-caused the false-negative to ONE leaf shape: after the Z4 interpolation consumes every summation variable, the recursion bottoms out at a nonempty 0-variable theta-CONSTANT sum Σ cᵢ·∏ θ(rational; p) that is genuinely ≡ 0 but had NO ZERO certificate (Z1 needs carrier cancellation, Z2/Z4 a LIVE variable, the N-detect only NONZERO). rc228 closes it with a genuine, sound, terminating certificate — never a numeric band.
- The certificate (a THEOREM, not a band): a theta-constant argument is a rational
ρ = ∏ ρ_ℓ^{v_ℓ}— a monomial in the DISTINCT PRIMES the interpolation substituted (unique factorization). Z5 LIFTS one such primeρ*back to a fresh elliptic variablev: the lifted single-variable objectL(v)satisfiesL(v = ρ*) = leafEXACTLY (substituting the integer prime back reproduces every coefficient), so if the exact Weierstrass ±-pair reduction (Z2 — Rosengren arXiv:1608.06161 Eq. 1.12, the value-faithful three-term rewrite already MPM-verified in the carrier) closesLto the EMPTY normal form, thenleaf = L(ρ*) ≡ 0by specialization (a specialization of an identically-zero elliptic function is zero). Z5 produces ONLY ZERO verdicts, never a NONZERO claim (a False from the ±-pair reduction is "not proven by a lift", never a nonzero assertion), and it is gated to the 0-variable leaf so 1-variable objects (the #693 / #771 witnesses) are untouched. Fast + terminating: every attempt is a bounded ±-pair reduction — NO interpolation, NO re-lift, NO recursion. thetasum._z5_theta_constant_zero(Python) slots UNDER Z1/Z2/Z3s/Z4 in_decide_struct, reached only when they decline: it factors the leaf's primes (_leaf_prime_set, Class-J trial division), lifts each into the fresh_Z5_SYMvariable (_lift_prime_terms, exact), and runs the existing_pair_reduce_component. A genuinely-NONZERO leaf hasL(ρ*) ≠ 0soL ≢ 0, and the SOUND reduction never reaches the empty form — no false zero.- Same-rc 1:1 C peer (
srmech_thetasum_interp.cti_z5_leaf+ helpers) mirrors the loop at the Cti_decide0-variable leaf: it REUSES a leaf-unused symbol slot (all slots are unused at a 0-variable leaf) as the lift variable — no new symbol, no ABI change — factors coeffs as int64 (the leaf constants are products of the small interpolation primes; a coeff beyond int64 makes Z5 not-applicable at that leaf, SOUND, and the arbitrary-precision pure oracle covers it), and drives the existingti_pair_reduce. The native peer GENUINELY computes Z5 in-C for a symbol-bearing 0-variable leaf (n_syms > 0); a degenerate n_syms=0 top-level constant object has no slot and routes to the pure Z5 via the arena-decline path (both arms return the same verdict). The Z2 pair-reduce VERDICT is orientation-invariant to the lift symbol's slot (verified), so native == pure regardless of slot choice. The parallel peer inherits Z5 through the sharedti_decide. Additive C symbols →SRMECH_ABI_VERSIONstays 4; the ws-bound grows by one transient lift-copy. - Soundness gate (all green): the #771 FALSE-ZERO regression witnesses A (a ±-pair family
Σ cᵢ·θ(uᵢx)θ(uᵢ/x), u=2..7) and B (Σ (aₜ⁴/∏_{s≠t}(aₜ−a_s))·θ(aₜx³), a=2..12) stayis_zero = False; a perturbed three-term identity stays False; the rc210 corpus parity (native == pure on all 126 objects — the corpus reaches NO 0-variable leaf, so Z5 never fires there) holds; and no object flips to a false zero. - The feasible-leaf proof that Z5 FIRES: the all-rational-constants Weierstrass three-term identity
three_term(a,b,c,x=const)— the pre-rc228 documented honest DECLINE (test_all_constant_three_term_honest_decline, retired) — is now CERTIFIEDis_zero = True(test_all_constant_three_term_z5_certified_rc228), on both the pure recursion AND the native-computed symbol-bearing case. Newtests/test_thetasum_z5_theta_constant_rc228.py(14): the parametrized feasible leaves, the lift mechanism, the Witness A/B guards, the perturbed-stays-False soundness, native-genuinely-computes-Z5, native == pure, and the honest (3, 3) residue. - HONEST RESIDUE (outcome C — the cap is UNCHANGED): the Aₙ (3, 3) leaf itself (3 terms × 29 thetas over the four primes ⅖/29/71) is the genuine high-kernel-rank residue Z5 does NOT reach — its per-term exponent matrices map
ℤ²⁹ → ℤ⁴with 25-dimensional kernels, and its arguments are NON-TORSION prime powers (log ρ / 2πiis a generic imaginary point, NOT a rational multiple of the period lattice), so there is NO modular / Sturm structure to bound the p-valuation and NO single-prime ±-pair lift closes it (measured: every lift declines Z2; a single-prime-lift full-decide is UNKNOWN in ~73 s). So_VERIFY_MAX_COMPOSITIONSSTAYS 6 (no in-budget certificate closes the (3, 3) verify; the (3, 3)is_zerois the honestFalse= "not proven", the safe direction, and Z5 declines it FAST — no hang). Closing (3, 3) needs a genuinely different algorithm for high-kernel-rank theta-constant sums; that is the queued residue.
Ledger: Z5 is INTERNAL to ThetaSum.is_zero — no new public op, no ToolEntry, no registry regen, no Rosetta ledger moves; tools.total stays 415; CEIL_PYTHON_ONLY_DEBT / CEIL_BIGNUM_REFERENCE / CEIL_NON_COMPUTE_OWED all HOLD at 0. Additive C symbols → SRMECH_ABI_VERSION stays 4. Build gcc-13.4 -O2 -DNDEBUG AND asserts-live -O1, zero warnings under -Wall -Wextra, no assert fires; JPL ratchet green (the new C functions are ≤ 60-line, ≥ 2 asserts, no goto/malloc, NULL-before-assert, Class-K sign). 5 SSOT files rc227 → rc228.
[0.9.0rc227]¶
The Aₙ (type-A / Milne) elliptic Jackson REDUCTION ROW — the sibling root-system member beside the Cₙ capstone, with the per-call proof (ships as 0.9.0rc227). The shipped Cₙ row's own _OPEN_HINTS named "an Aₙ (or other root-system) elliptic multisum" as its next frontier; rc227 ships it as a GENUINE ENGINE (decompose-and-COMPUTE the reduction + PROVE it per call — never a brute-force finder, never a shell). New module srmech.amsc.elliptic_jackson_an with BOTH sides of the identity first-class (the rc216 precedent).
- The identity (MPM-verified at build from the extracted PDF, sha256
299d2738c4539a390a437c795a0b0084a5c82d403566c4f549db39482e3076ce): Hjalmar Rosengren, "New transformations for elliptic hypergeometric series on the root system Aₙ", arXiv:math/0305379v1 [math.CA] (27 May 2003), Eq. (6) — "[R4, Theorem 5.1], … an elliptic analogue of Milne's Aₙ Jackson summation" (the m = 1 case of the paper's Thm 3.1 elliptic Kajihara transformation). Over the SIMPLEXy₁+…+yₙ = N(C(N+n−1, n−1)compositions — a DIFFERENT index set from Cₙ's partitions), with the type-A Weyl denominatorΔ(z) = ∏_{j<k} zⱼ·θ(zₖ/zⱼ), its shift ratioΔ(z·q^y)/Δ(z) = ∏_{j<k} q^{yⱼ}·θ(zₖq^{yₖ}/zⱼq^{yⱼ})/θ(zₖ/zⱼ), and the COMPUTED balancingw = z₁⋯zₙ·a₁⋯a_{n+1}(never a free input — the Aₙ analogue of Cₙ's computede):Σ_{|y|=N} Δ(zq^y)/Δ(z)·∏ₖ∏ⱼ(aⱼzₖ)_{yₖ}/[(wzₖ)_{yₖ}·∏ⱼ(qzₖ/zⱼ)_{yₖ}] = ∏_{j=1}^{n+1}(w/aⱼ)_N / [∏_{j=1}^{n}(wzⱼ)_N·(q)_N]. Re-verified at build IN EXACT ℚ at p = 0 (θ → 1 − z; both sides collapse to identical exact rationals for every (n, N) ≤ (3, 3) — the basic/Milne case), pinned forever by an in-repo p = 0 exact test. multivariate_elliptic_jackson_an(z, a, q, N, *, verify=False)— the RHS closed-form EllRatio (VARIABLE-ARITY operand:zthe length-n vector,athe length-(n+1) vector — unlike Cₙ's fixed 6 scalars). Withverify=True:{"closed_form", "verified"}where the proof builds the symbolic LHS and decides(LHS − RHS).is_zero— the rc98/rc99 COMPLETE multi-variable elliptic decision. PROVEN END-TO-END:verified is Trueon every (n, N) with ≤ 6 compositions — (1,1..3), (2,1), (2,2), (2,3), (2,4), (3,1), (3,2), (4,1) — on BOTH the native AND the pure is_zero arms (the Aₙ summand is far LIGHTER than Cₙ's: no quadratic-argument VWP thetas, so the measured frontier sits beyond the Cₙ row's 4-partition cap). A perturbed closed form is caught (False— the check has teeth); beyond the cap the op returns the honestverified=Noneinstantly (the Cₙ contract). ⚠ Then = 1case is a trivial single-term degeneration (LHS ≡ RHS term-by-term via the balancing) — NOT the ₈ω₇; the genuine proof burden is n ≥ 2, which the tests pin.an_vwp_multisum_lhs(z, a, q, N)— the LHS simplex sum built SYMBOLICALLY as an exact ThetaSum (ascending lexicographic composition order; the Vandermonde monomial part∏_{j<k} q^{yⱼ}in the Class-K EllRatio prefactor branch, neverabs()).- The
sigma_elliptic_andispatch row (F929): tag aliases (sigma_elliptic_an/an/an_jackson/an_elliptic_jackson/milne_an/milne) + the 4-key structural sniff (z/a_vec/q/N— the variable-arity vector pair; never collides with the Cₙ 8-key set) →_try_sigma_elliptic_anroutes to theverify=Truereducer, surfaces the REALverifiedstatus, and routes aFalseto OPEN (the anti-hallucination gate)._OPEN_HINTS["sigma_elliptic_multivar"]updated (the Aₙ row is now SHIPPED — tagrow='sigma_elliptic_an'); the new_OPEN_HINTS["sigma_elliptic_an"]names the next frontier honestly (Dₙ/BCₙ, higher-genus theta multisum, or the m ≥ 2 elliptic Kajihara TRANSFORMATION of which Eq. 6 is the m = 1 case). - Same-rc 1:1 C peers:
srmech_an_vwp_multisum_lhs(+_ws_bound) — the per-composition TERM EllRatios byte-exact over the sharedsrmech_ellbase_*monomial algebra +er_build, in the same ascending-lex composition order (a composition ODOMETER, distinct from Cₙ's partition odometer), the Python side summing the forms into the ThetaSum — andsrmech_multivariate_elliptic_jackson_an(+_ws_bound) — the single closed-form EllRatio. VARIABLE-ARITY wire: the z/a vectors ride as parallel bigint arrays + flat int32 exponent rows (thesrmech_elliptic_cauchy_determinantconvention). Both dispatch through_native.py(has_native_*+ wrappers), trusted only after==the pure result (the complete alternative + parity oracle). Malloc-free caller-arena, JPL-clean, Class-K sign branches. Additive symbols →SRMECH_ABI_VERSIONstays 4. - Tests (
tests/test_an_elliptic_jackson_rc227.py): the end-to-endverified is Truebattery (8 feasible (n, N) incl. n = 2, 3, 4 cross-variable); the n = 1 trivial-degeneration pin (LHS == from_ellratio(RHS) exactly — documented NOT-₈ω₇); perturbed → False ×2; beyond-cap → None instantly ×2; the p = 0 exact-ℚ MPM re-verification in-repo; native == pure parity for both ops (+ clean decline); validation contracts; the router battery (tag + sniff + malformed→OPEN + beyond-cap None + Cₙ-uncollided + both hints updated); registration (ToolEntry ×2 / tools.total 415 / Rosetta rows / responsion edges). - HONEST LIMITS (the measured frontier + an upstream finding): the verify cap is
_VERIFY_MAX_COMPOSITIONS = 6(all cases at or under it PROVE True on both arms, < 1.3 s pure / < 0.2 s native). At the NEXT frontier size — (3, 3), 10 compositions — the build-time measurement found the native interpolationis_zeroreturning a fastFalseon a residual that the p = 0 exact check AND a small-p truncated evaluation (deviation ~10⁻¹⁵ ≈ truncation scale) show is genuinely≡ 0, while the pure oracle does not decide within 600 s. That suspect native verdict at 10-term scale is logged as an UPSTREAM ThetaSum.is_zero finding (the #693 degree-band discussion's multi-variable sibling — likely an under-provisioned decline path surfacing as False rather than an error); the rc227 cap honestly stops BELOW it, so no in-cap verdict depends on the suspect region. - POST-SHIP DIAGNOSIS (2026-07-12, doc-only — the (3, 3) suspect verdict RESOLVED): the build-time guess "likely an under-provisioned decline path" above is REFUTED by instrumenting the certificate recursion on the (3, 3) residual (11 terms, ONE joint character). Every Z4 interpolation frame receives its FULL
D+1pairwise-distinct nodes (substitution path a1→a2→a3→a4→z2→z3→z1→q with per-frame v-degrees D = 9/6/6/6/13/19/22/70 — the node-shortage branch NEVER fires; nothing is under-provisioned), and the recursion bottoms out where all variables are consumed: a nonempty 0-variable theta-CONSTANT sum (3 terms × 29 rational-argument thetas) that is genuinely zero (exact p-expansion identically 0 through order 80) by a nontrivial theta-constant identity — a shape the certificate system has NO ZERO certificate for (Z1 needs exact carrier cancellation, but 3 terms survive combine; Z2/Z4 need a live variable; N2 detection only ever proves NONZERO). So the fastFalseis the honest "cannot certify" — the #695 multivariate-interpolation COMPLETENESS WALL, sharpened to its root cause: the 0-variable LEAF certificate gap, not the interpolation step.is_zerois SOUND but INCOMPLETE past the cap (False= "not proven", the safe direction — no false-positive surface anywhere in this), the native and pure verdicts AGREE (the Cti_decideDFS short-circuits at the first unproven leaf in ~0.7 s; the pure three-valued recursion exhaustively evaluates every child of every Z4 frame — ~10⁹ frames at (3, 3) — hence the 600 s non-finish; an early-exit pure mirror of the same body returns the same verdict in ~14 s), and_VERIFY_MAX_COMPOSITIONS = 6is CORRECT (no cap lift is sound: widening provisioning cannot close a certificate gap — closing it needs a NEW zero certificate for theta-constant sums, i.e. the #695 "different algorithm"). Doc-only: no code change, no version bump.
Ledger: two new public ops → Rosetta rows c_dispatched FROM BIRTH (both dispatch to their same-rc C peers) — CEIL_PYTHON_ONLY_DEBT / CEIL_BIGNUM_REFERENCE / CEIL_NON_COMPUTE_OWED all HOLD at 0 (untouched). tools.total 413 → 415 (the 34 count assertions across the suite updated). The rc184 tool registry + rc225 responsion registry REGENERATED (the new ToolEntries + the new verified/open responsion edges + the edited _OPEN_HINTS payloads; byte-ratchets green). The rc225 responsion schema gains the multivariate_elliptic_jackson_an⊗EllMonomial verified edge + the sigma_elliptic_an open edge (answers_with verbatim from _OPEN_HINTS). Build gcc-13.4 -O2 -DNDEBUG AND asserts-live -O1, zero warnings, no assert fires. 5 SSOT files rc226 → rc227.
[0.9.0rc226]¶
The genus-2 Fay/KP RE-INDEXING CERTIFICATE — addition_holds upgraded from a safe-region boolean to an explicit, EVERY-ORDER, inspectable witness (ships as 0.9.0rc226). The rc73 addition_holds / rc88 addition_holds_at gates decide the genus-2 theta ADDITION / Fay-Hirota-shadow bilinear identity (DLMF §21.6.8, z=0: θ[a;0](Ω)·θ[b;0](Ω) = Σ_{r∈(ℤ/2)²} θ[(2r+a+b)/2;0](2Ω)·θ[(2r+a−b)/2;0](2Ω)) only on the conservative safe inner region A, B, |C| ≤ 2·box². rc226 makes the identity's own PROOF first-class: the new CARRIER METHOD RiemannTheta.fay_reindexing_certificate(a, b, box=8) -> dict returns the re-indexing certificate — the parallelogram/diagonal bijection φ: (m,m') ↦ (M,M') = (m+m', m−m') on the FULL index lattice ℤ²×ℤ² made explicit, with the closed-form facts that make the identity hold TERM-BY-TERM at EVERY ORDER (no truncation, no safe-region cut).
- The every-order proof (closed-form, in-method — never a region check wearing a certificate's name): (a) the PARALLELOGRAM law
2u_d² + 2u'_d² = (u_d+u'_d)² + (u_d−u'_d)²(per coordinate) + the polarized cross form2u₁u₂ + 2u₁'u₂' = (u₁+u₁')(u₂+u₂') + (u₁−u₁')(u₂−u₂')verified as EXACT polynomial identities in CANONICAL MONOMIAL FORM overℤ[u₁,u₂,u₁',u₂'](_fay_parallelogram_exact/ module_fay_expand_quadratic— the variables are FREE, so the verdict is index-INDEPENDENT: every lattice index, every order; explicitly NOT sampled); (b) φ's round-trip + parity-coherence + sector-characteristic identities (s⁺+s⁻ = 4r+2a,s⁺−s⁻ = 2b) verified as exact LINEAR identities in canonical form (_fay_bijection_exact/_fay_linear_zero) — φ is a bijection onto the parity-consistent pairs, partitioned byr = M mod 2 ∈ (ℤ/2)²= EXACTLY the RHS r-sum (sector indices land atU ≡ 2r+a+b,U' ≡ 2r+a−b (mod 4)); © coefficient preservation pinned against the SHIPPED_spec_additiongate spec (every factorevec = 0, every product sign+1→ each index contributes exactly+1on both sides, index-independently).every_order=TrueONLY when (a)+(b)+© hold AND every computational cross-check agrees. - The strengthening evidence (
beyond_safe_region_witness): a CONCRETE monomial — the key of the LHS index(n,n') = ((box,0),(box,0)), diagonal exponent≥ 16·box², strictly beyond the old2·box²gate — whose FULL exact coefficient the certificate resolves on BOTH sides (complete, not truncated: the non-negative diagonal exponents bound every contributing index; e.g.a=(1,0), b=(0,0), box=8→ monomial(1090, 0, 0)with coefficient 8 = the two essentially-different representations545 = 17²+16² = 23²+4²× 4 sign combinations each, distributed by φ across the r-sectors as33²+1² = 27²+19² = 1090with the mod-4 classes picking the sector).addition_holds's region compare cannot see this monomial. - Consistency (byte-exact, the no-shell gate): on the same
(a, b, box)the certificate re-runs BOTH existing deciders — the denseaddition_lhs == addition_rhscompare restricted to the safe region (reusing the rc73 lattices) AND the sparse_sparse_decidepush-down (reusing the rc107 nativesrmech_riemann_theta_gate_decide) — and reportsconsistent_with_safe_region_gate. The bounded window illustration (window_bijection_ok/window_tuples_checked: key-equality under φ + mod-4 sector congruences + φ⁻¹ round-trip per tuple) is labeled what it is — an ILLUSTRATION, not the proof. - Same-rc 1:1 C peer:
srmech_riemann_theta_fay_certificate(srmech_riemann_theta.c) verifies the exact structural facts natively — the closed-form parallelogram identity over a 10-slot canonical degree-2 coefficient vector, the bounded window bijection, and the FULL exact witness coefficients on both sides (reusing the file'srtgate_isqrt; box ≤ 2047 = the derived int64 window bound; witness key gated by the box-derived diagonal cap + the AM-GM2|C| ≤ A+Bcross bound, Class-K sign branch). The Python method dispatches to it when loaded (_native.riemann_theta_fay_certificate_c, hasattr-guarded) and falls to the pure bodies otherwise (the COMPLETE alternative + the parity oracle); the closed-form proofs always run in Python — they ARE the certificate's logic. JPL-clean (bounded loops, no malloc, ≤60-line functions, ≥2 asserts per function, NULL-before-assert on caller pointers). - The honest scope (
fay_trisecant_scope_note, the certificate dict'sscopefield): REPRESENTABLE (built): the every-order re-indexing certificate for the ABSTRACT addition / KP-Hirota-shadow bilinear. OPEN (NOT built, never fabricated): the CURVE-SPECIFIC Fay trisecant identity — prime formE(·,·)+ four curve points, holds ONLY for theta of Jacobians (Fay 1973; Mumford, Tata Lectures on Theta I/II) — whose holding IS the is-Jacobian / Schottky condition (Krichever 2006, the trisecant characterization; Shiota 1986, θ-solves-KP; anchor Grushevsky–Xie arXiv:2504.20243), genuinely open for genus ≥ 5. The carrier's rational Ω is generically non-Jacobian and a genuine Jacobian Ω is transcendental, so the trisecant proper CANNOT be witnessed as an exact identity here — mathematical content, not a routing limitation. NO code path claims is-Jacobian / trisecant-decided. - Tests (
tests/test_fay_reindexing_certificate_rc226.py): every genuinea ≠ bpair (+ thea = bduplication collapse) getsevery_order=Truewith the exact quadratic-form verdict; the certificate agrees withaddition_holds/ the dense restricted compare on the shared region; the witness monomial is strictly beyond the safe region, resolved with equal nonzero coefficients on both sides, and invisible to the restricted dense compare; the(1090, 0, 0)worked example pinned; native == pure parity on the C peer's five outputs; the scope string names the g≥5 Schottky OPEN and does NOT claim is-Jacobian; native present + asserts-live safe.
A CARRIER METHOD (the addition_holds / addition_holds_at / kp_bilinear_scope_note precedent) — NO new ToolEntry, tools.total stays 413, no registry regen, no rosetta ledger moves, CEIL ratchets untouched; adding a symbol does not bump ABI → SRMECH_ABI_VERSION stays 4. Exact integer throughout (no float, no abs() — Class-K sign branches). Build gcc-13.4 -O2 -DNDEBUG AND asserts-live -O1, zero warnings, no assert fires. 5 SSOT files rc225 → rc226.
[0.9.0rc225]¶
responsion_schema — the k=3 introspection completion: the stored-relationship face (user design 2026-07-12; ships as 0.9.0rc225). srmech = Stored-RELATIONSHIP Mechanism — yet only two of its three faces were introspectable: tool_schema exposes the OPS (the verbs) and carrier_schema (rc205) the OPERANDS (the nouns). The relationships — the thing the mechanism is named for — had no introspection face. srmech.amsc.responsion_schema.responsion_schema() is that face: the k=3 completion NOT as a third peer-list but as the EDGE binding the k=2 pair of nodes — this op, on this operand, answers THIS way (op⊗operand⊗responsion; F1131/F1186).
- The shape (the key IS the edge): every entry is keyed by the
"<operator>|<carrier>"pair —operatora realtool_schemakey,carriera realcarrier_schema()key, BOTH validated at derivation time (a dangling ref raises; never ships silently) and carried first-class in every responsion. One edge can carry multiple responsions, so the value is a list (deterministically ordered by kind/status/text). A flat bare-name registry would flatten the one thing that is definitionally an edge — the rc224 flatten-trap, one algebra up — deliberately not built. - Two regimes of ONE responsion, held in unity:
discrete_algebraic= the F929 reduce-back rows (dispatch.py): the verified edges map each shipped reducer to its operand carrier (gosper⊗Poly, zeilberger/wz_certificate⊗BiPoly, apagodu_zeilberger⊗TriPoly, q_gosper⊗QPoly, q_zeilberger/q_wz_certificate⊗QBiPoly, elliptic_wz_certificate⊗EllRatio, multivariate_elliptic_jackson⊗EllMonomial, resonant_spectrum⊗Mat, the_one⊗One — 11 verified closed-form edges), and the OPEN edges are the rows' honest residues: theinferrouter on each row's operand carrier withanswers_withtaken VERBATIM fromdispatch._OPEN_HINTS(7 open_sustain edges — the F934 honest sustain in the schema; editing a hint changes the canonical payload so the hash-ratchet forces the C table to follow).continuous_spectral= the response-function ops:laplacian.responsion's propagatore^{−zL}·u0⊗ resolvent(zI−L)^{-1}·u0Laplace-dual pair on the ONEresponsion|Matedge, pluspropagate(the named EPH surface),heat_trace(Θ(t) = Tr e^{−tL}, kind=trace), andground_state_flux_response(λ_min(Φ), kind=response_curve). 22 edges / 23 responsions total. The genome tie-back: storage = the carrier (the generator L), query = the op (excite), response = the responsion. - Same-rc 1:1 C peer:
srmech_responsion_schema+srmech_responsion_registry_{count,get,find}over the compiled-insrmech_responsion_registryconst table (GENERATED by the newc/tools/gen_responsion_registry.py— the rc205 carrier-registry codegen model; entries in byte-sorted edge-key order, per-edge payloads baked pre-canonical compact-ASCII). The assembler output is BYTE-IDENTICAL tojson.dumps(_pure_responsion_schema(), sort_keys=True, separators=(",", ":"))— the sha256 hash-ratchet locking the C table to the Python SSoT, exactly the carrier_schema contract. Accessors + assembler in the newsrmech_responsion_schema.c(malloc-free, caller-buffer/size-query two-pass, JPL-clean); struct + prototypes insrmech.h.responsion_schema()native-dispatches when the C peer is loaded and no profile tools are registered (the carrier_schema gate), pure fallback complete. - Python-side (
_native.py):_SrmechResponsionEntryCstruct +_bind()argtypes/restype declarations (the rc201 lesson) +has_native_responsion_schema/responsion_schema_json_c/responsion_registry_{count,keys,find}_chelpers, all hasattr-guarded (a stale lib keeps the rest of the native surface). - Tests (
tests/test_responsion_schema_rc225.py, 26): the edge shape (key ==operator|carrier; never a bare name; the 6-field entry contract); no dangling refs (every operator resolves in tool_schema, every carrier in carrier_schema — the k=3 binds the k=2 — plus a monkeypatched dangling ref RAISES at derivation); both regimes present; the propagator⊗resolvent Laplace-dual pair on the one edge; the honest OPEN (≥1 open entry; every openanswers_withverbatim from_OPEN_HINTS; spot-pinned sigma_elliptic + spectral residues); the hash-ratchet (C bytes == Python SSoT pre-image, sha256-locked viaformat.sha256_bytes); count/keys/find round-trips; native wrapper == pure VALUE-identical; codegen idempotence + pure-ASCII + every-edge coverage; ToolEntry registered + tools.total 413 + the Rosetta composes_c row + describe() total. - Collateral: the rc184 tool registry REGENERATED for the new ToolEntry (its byte-ratchet green); carrier_schema rc205 + tool_schema ops rc185 + rosetta completeness/transitive-standalone + JPL audit + infer router f929 / rc176 / rc192 / rc223 / rc224 + version pin — all green (260 in the core sweep); the 53
tools.total == 412assertions across 44 files updated to 413 (legitimately: one new public op).
Ledger: new public op srmech.amsc.responsion_schema.responsion_schema → Rosetta row non_compute/composes_c FROM BIRTH (dispatches to its C peer, composes tool_schema + carrier_schema + the const table; no new math) — CEIL_PYTHON_ONLY_DEBT / CEIL_BIGNUM_REFERENCE / CEIL_NON_COMPUTE_OWED all HOLD at 0 (untouched); the living non_compute split pins bumped composes_c 120 → 121 / total 189 → 190 (test_non_compute_ratchet_rc170 + test_annex_ratchet_rc177/rc183 — the rc205 carrier_schema precedent). tools.total 412 → 413. Additive C symbols + one struct → SRMECH_ABI_VERSION stays 4. Build gcc-13.4 -O2 -DNDEBUG AND asserts-live -O1, zero warnings, no assert fires. 5 SSOT files rc224 → rc225.
[0.9.0rc224]¶
#796 CLOSED — the LAST infer row (spectral) dispatches in C via the EXACT operator-verdict (ships as 0.9.0rc224). The naive spectral row would have been a FLOAT eigensolve with a within-tolerance verdict — wrong (last-ULP cross-platform divergence). rc224 ships the exact design instead: the spectral verdict is an exact operator-level structural fact — the reduction EXISTS iff L is real-symmetric (the spectral theorem's own hypothesis), checked BIT-EXACT (L[i][j] == L[j][i] IEEE equality over all pairs). The eigenvalues are the OPERAND (the resonant_spectrum payload), never the verdict — platform-stable with NO float in the decision.
- The key recognition (proven empirically, 400/400): the old
_try_spectralverdict checkedΛ² ≈ L·Lon theresonant_spectrumforce-orders. Butl1 = V·diag(Λ)·Vᵀ,l2 = V·diag(Λ²)·Vᵀ, andl1·l1 == l2EXACTLY wheneverVᵀV = I— the check was a TAUTOLOGY of the spectral theorem; in float it was only an eigensolve-quality gate (residual ~2e-14), never a verdict. Measured over 400 random symmetric Laplacians: the float verdict was ALWAYS reducible, NEVER None, and agreed 400/400 with the exact predicate "is L bit-exact real-symmetric". So the verdict IS the symmetry predicate — no eigensolve needed to DECIDE. - Python
_try_spectral(dispatch.py): the verdict is now the bit-exact real-symmetry check on the built L (L[i,j] == L[j,i]over all pairs — a symmetry PREDICATE; the diagonal self-compare is False only for a NaN). Symmetric →resonant_spectrum(L, orders=2)is materialised as the closed-form PAYLOAD and the row reduces; asymmetric → the honest OPEN. The float_matrices_close(Λ², L·L)verdict-gate is REMOVED (it was the flattening; the helper is gone). Existing spectral verdicts are UNCHANGED on the whole corpus (the 400/400 above; the F929 router suite passes untouched). - C spectral row (
srmech_infer.c): the payload's f64 leaves ride the wire as IEEE-754 bit patterns (one signed int64 per double — the bit-EXACT float wire; −0.0 = INT64_MIN; no decimal float parse, no strtod, in the decision path). L is built in C per payload shape:edges(+weights,n) → the Class-Lsrmech_graph_dense_laplaciankernel (the SAME builder the pure path dispatches to — identical accumulation order); explicitlaplacian/matrix→ the raw grid read;adjacency→ the in-place D−A transform in the pure_build_laplacian's exact float-op order. The verdict is the bit-exact symmetry predicate — NO eigensolve, NOresonant_spectrumcall, NO float tolerance in C. Symmetric → the structural literal{"reducer":"resonant_spectrum","reducible":true,"row":"spectral","verified":true}(the eigenvalue payload is re-derived pure-side in_finish_native, the rc223 safety-net pattern); valid-but-asymmetric → the DEFINITIVE{"reducible":false,"row":"spectral"}(the C-built L is entry-for-entry the pure build, so the pure predicate reads identically); malformed/unbuildable → non-OK → the COMPLETE pure infer decides. New dedicated sizersrmech_infer_spectral_arena_bytes(rel_len, n)(parse + ONE n×n double grid + the edge arrays — no eigensolve scratch); a monstrous n declines pastSRMECH_INFER_WS_CEILING_MBto pure.srmech_inferrefactored (spectral/rc223/classic route helpers) to stay JPL Rule-4 clean. - Why native == pure is PROVABLE on every platform: the verdict compares only (a) marshalled bit patterns (identical by construction), (b) exact IEEE negations, and © additions performed in the SAME order as the pure builder. The marshal DECLINES non-finite leaves (inf/NaN → pure), and finite accumulation can overflow to ±inf but never to NaN — so no entry's IEEE self-equality can break on one arm and not the other. There is no borderline and no float tolerance anywhere in the decision.
- Python-side (
_native.py/dispatch.py):_marshal_relationshipgains the spectral wire (bit-pattern int64s; declines non-finite payloads + unbuildable shapes);has_native_spectral_row()probes the rc224 surface (hasattr-guarded — a stale lib falls to pure);infer_croutes the spectral wire to its own sizer + ceiling;_finish_nativerebuilds via the SAME pure_try_spectral. - Tests (
tests/test_infer_spectral_rc224.py, 26): native verdict == pure verdict (byte-identical fields, never float-eigenvalue identity) for edges/matrix/adjacency reducible cases + the asymmetric-matrix OPEN + a non-spectral payload; the exactness proof — two symmetric Laplacians with spectra 400 orders of magnitude apart (1e−200 vs 1e+200, far outside any eigensolve) both return the raw C reducible:true, and a 1-ULP nudge of ONE off-diagonal entry flips the C verdict to the definitive false on both arms; a 40-case seeded battery (edges + the explicit-matrix form of the same L + 1-ULP-perturbed asymmetric twins) native==pure; the safety rails (non-finite declines the marshal → pure; n=10000 declines past the ceiling; ragged/empty/out-of-range/mismatched payloads stay OPEN on both arms); the wire is bit-patterns-only (no decimal floats; −0.0 = INT64_MIN). rc176's fall-to-pure list updated (spectral now marshals; its cases stay in the parity sweep). Collateral green: infer rc176/rc192/rc223 + router f929 + chain-infer rc175 (206 w/ JPL), resonant_spectrum rc37 + laplacian parity/numpy-free/standalone-honor + coupling (62), pi rc19 + bignum rc35 + qmat rc34/40/48 + count tests (200 + 1 pre-existing skip). Build gcc-13.4-O2 -DNDEBUGAND asserts-live-O1, zero warnings, no assert fires. - HONEST LIMITS: the exactness claim covers the VERDICT (reducible/row/reducer) — the eigenvalue PAYLOAD is still a float eigensolve with its usual platform envelope, re-derived pure-side. A pre-existing (rc223-verified) pure-vs-native robustness gap in the payload op remains: on overflow-scale matrices (~1e300) the pure Jacobi eigensolve raises
OverflowErrorwhere the native kernel converges, so an infer() run with the native lib loaded can reduce a payload the no-native configuration reports OPEN — that divergence is insideresonant_spectrum's internal eigensolve dispatch (both arms of any SINGLE configuration agree; the rc224 C decision plays no part in it) and predates this rc unchanged. The bit-exact symmetry predicate is strict by design: a Laplacian stored with a 1-ULP asymmetry is honestly OPEN (store symmetric operators symmetrically).
No new public op (the spectral row + sizer are additive C symbols with no ToolEntry; infer stays composes_c) → tools.total stays 412, no registry regen, no rosetta ledger moves, no CEIL moves; adding symbols does not bump ABI → SRMECH_ABI_VERSION stays 4. 5 SSOT files rc223 → rc224.
[0.9.0rc223]¶
#796 — the three remaining EXACT-ℚ infer rows dispatch in C for a bare-C host (ships as 0.9.0rc223). rc176 put the F929 OPEN/infer router's detect + dispatch + verify LOGIC in C for two rows (cyclic → the_one, sigma-gosper → gosper); rc192 added the sigma-definite wz_certificate row over the rc191 srmech_carrier_read_bipoly reader. rc223 closes the remaining exact-ℚ rows (the numeric spectral/resonant_spectrum row is deferred to rc224):
- Foundation — three new PUBLIC carrier readers (
c/src/srmech_carrier_marshal.c, followingsrmech_carrier_read_bipolyexactly; bignum-decimal-string coefficients, the rc191 marshal convention; malloc-free, caller marshal-arena, JPL-clean):srmech_carrier_read_tripoly(the j/k/n nested_tri_pairsbridge, lowered to the flat j-major +nlen[dj*kdeg+dk]gridsrmech_apagodu_zeilbergerconsumes; ragged j-blocks pad rectangular exactly like the Python_az_tri_flatten),srmech_carrier_read_qbipoly(the Y-list of[x_low, [q-run, …]]_qb_pairsbridge, lowered to the flat Y-major/X-major q-runs +qlen[]/xlow[]/xcells[]/ycellsthesrmech_q_zeilberger/srmech_q_wz_verify/srmech_q_gosperpeers consume; a QPoly rides as ONE Y-cell), andsrmech_carrier_read_ellratio(the PRE-INTERNED EllRatio wire — the newsrmech_ellratio_wire_t— n_syms + x/p/q/y/N/K indices + flat exact-ℚ coeffs + int32 exponent rows; the interning is done Python-side in the sorted-symbol_EWZ_FORCE_SYMSconvention so the reader is a pure array lowering). The rc191 round-trip prover gains the matching kinds (SRMECH_CARRIER_TRIPOLY/_QBIPOLY/_ELLRATIO) proving marshal→read→canonical re-serialisation lands every (bignum) coefficient + the nesting. - The three rows in
srmech_infer.c(each: detect shape → read carriers → run the EXISTING C reducer → VERIFY its own contract → emit the same small decision JSON the pure infer path consumes):sigma_multivar(six (n,j,k) TriPoly term-ratios →srmech_apagodu_zeilberger@max_order=1; a has=1 minimal-order recurrence IS the verification; has=0 is NOT definitive → non-OK → pure),sigma_qdefinite (four QBiPoly q-term-ratios → FINDsrmech_q_zeilberger@order-1, accept only the q-WZ shape a₀+a₁=0 nonzero rational scalars, PROVEsrmech_q_wz_verifyon the 1/a₁-rescaled certificate — the rc192 wz shape, one base-axis rung up; a FIND decline → pure, a found-but-not-WZ/verify-fail → the definitivereducible:false),sigma_qindefinite (the QPoly q-term-ratio as a one-Y-cell wire →srmech_q_gosper; has=1 IS the verification), andsigma_elliptic(the ₈ω₇ EllRatio →srmech_elliptic_wz_certificate; has=1 = recognized AND the connection-coefficient certificate decided ≡0). Three new dedicated arena sizers (srmech_infer_sigma_{multivar,q,elliptic}_arena_bytes) size each row on its ACTUAL shape/limbs; the Pythoninfer_cDECLINES pastSRMECH_INFER_WS_CEILING_MB(default 256 — the apagodu dense-RREF arena is ~350+ MB even for small genuine systems, theSRMECH_AZ_WS_CEILING_MBhonor one level up) so the bounded-memory pure CRT path stays the everyday multivar decider. - THE SAFETY NET (identical to rc192): a wrong reader/wiring CANNOT emit a wrong decision. ANY failure — arena overflow, malformed operand, reader non-OK, reducer non-OK, a non-definitive reducer decline, or a shape that isn't one of these rows — returns non-OK → the Python caller runs the COMPLETE pure infer. Rows whose C reducer declines non-definitively NEVER emit
reducible:false; everyreducible:trueis rebuilt through the SAME pure_try_*reducer in_finish_native(a defensive disagreement → the honest OPEN). Never an unverified reduction. - Python-side (
dispatch.py+_native.py):_marshal_relationshipgains the three row marshals (TriPoly/QBiPoly/QPoly bridge forms + the pre-interned EllRatio wire, bignum-safe decimal strings);_finish_nativerebuilds via the row-matched_try_sigma_multivar/_try_sigma_q/_try_sigma_elliptic(with the pure body's own exception guard);infer_croutes each row to its own sizer + ceiling (and declines a MemoryError alloc to pure);has_native_exact_rows()probes the rc223 surface (hasattr-guarded — a stale lib falls to pure). - Tests (
tests/test_infer_exact_rows_rc223.py, 44): per-row native==pure parity over a genuine reduction AND an honest-OPEN case (FULL==where comparable), genuine C engagement (the ₈ω₇ + the q-geometric return the verified native decision; the k-free non-WZ q-definite returns the nativereducible:false; the multivar declines at the default ceiling and — opt-in — dispatches natively with it raised, proven on the dev box in 1.2 s), reader round-trips (2¹²⁷/10²⁵-scale bignum coefficients, negative Laurent x_low, ragged-grid padding) + malformed declines, sizer monotonicity, numpy/math-free source. Collateral re-verified green: infer rc176/rc192 + router f929 + elliptic router rc92 + Cₙ-multivar rc97 + chain-infer rc175 (139), apagodu rc53 / q_gosper rc55 / q_wz rc57 / wz rc43 / elliptic_wz rc91 / elliptic_gosper / tripoly rc52 (79 + 3 pre-existing skips). Build gcc-13.4-O2 -DNDEBUGAND asserts-live-O1, zero warnings, no assert fires; JPL ratchet green. - HONEST LIMITS: the native q-definite FIND covers only the k-free q-geometric class (the rc56
srmech_q_zeilbergernative scope), so genuine q-WZ pairs (nontrivial r_k) fall to pure — the row's native win today is the definitive k-free OPEN + the verify plumbing; widening the FIND is the owed everything-mirrors backlog. The multivar row's dense-RREF arena keeps it behind the ceiling by default (pure CRT is faster AND smaller; the C dispatch is proven but opt-in). The elliptic row conservatively falls to pure on has=0 (never a possibly-divergent native false). The elliptic-multivar Cₙ Jackson row stays pure (its per-call proof is carrier-symbolic); the numeric spectral row is rc224.
No new public op (the readers + sizers are additive C symbols with no ToolEntry) → tools.total stays 412, no registry regen, no rosetta ledger moves; adding symbols does not bump ABI → SRMECH_ABI_VERSION stays 4. 5 SSOT files rc222 → rc223.
[0.9.0rc222]¶
van Hoeij LLL knapsack recombination — the exponential Zassenhaus subset wall gets its known real fix, byte-identically (ships as 0.9.0rc222). factor_integer_poly's recombination step tried subsets of the Hensel-lifted modular factors — WORST-CASE EXPONENTIAL in the modular-factor count n (measured: Swinnerton-Dyer SD5 = minpoly(√2+√3+√5+√7+√11), deg 32, 16 quadratic modular factors → 39 207 candidates ≈ 13 s pure / ≈ 4.7 s native; SD6, deg 64, squares it to ≈ 2³¹ candidates — infeasible). rc222 replaces the wall with van Hoeij's polynomial-time LLL knapsack (M. van Hoeij, J. Number Theory 95 (2002) 167–189; expository J. Klüners in Springer The LLL Algorithm, 2010 — the construction was extracted from the actual papers and lodged with source URLs + sha256 in docs/srmech/notes/rc222_vanhoeij_attestation.md, the MPM discipline against a hallucinated lattice). A SPEEDUP, never a new answer: measured SD5 ≈ 0.72 s pure / ≈ 0.74 s native (≈ 18× / ≈ 6×), SD6 ≈ 35 s where the walk cannot finish — output byte-identical to the subset enumeration on every corpus input, by construction AND by test.
- The attested construction (both arms, mirrored exactly): scaled Newton traces
lc^i·Tr_iof each lifted factor via the Newton identities modp^k(paper eq. (2); non-monic scaling per §2.3 remark 4), per-trace boundsB_i = N·(|lc|·B_rt)^ifrom an exact-integer root bound (min of Cauchy and Fujiwara-1916, every intermediate CEILed — integer k-th-root ceilings, no float), the two-sided cutC^{a_i}_{b_i}(Definition 2.2 / eq. (8): symmetric remainder modp^{b_i}, exact shift-down, symmetric remainder modp^e), the knapsack lattice[[C·I_n | cuts], [0 | p^e·I_s]](§2.1) withC = ⌊isqrt(s·n)/2⌋balancingM² = C²n + s(n/2)², ONElll_reduce(δ = ¾), the EXACT Gram–Schmidt cutoffr = min{r : ‖V*_k‖² > M² ∀k>r}(LLL-paper (1.11); exact4·num > 4M²·deninteger compares — stronger than the paper's float-plus-error-bound variant), and a column-equality block decode (columns of the kept/Cprojections are equal iff same factor block whenL′ = W;#classes == rrejectsL′ ≠ W— equivalent to the paper's rref condition A, proof sketch in the attestation note). - The safety net (byte-identity by construction): the recombination is PHASED van Hoeij's own way (§2.2 steps 1–3): (A) subset sizes ≤ 3 only — peels every small block, so easy inputs never build a lattice; (B) the knapsack on any ≥ 8-factor remainder, its decoded blocks REPLAYED through the SAME candidate/exact-ℤ-trial-division code in the SAME (size, lexicographic) order the subset walk uses, INCLUDING the subset-cap and half-bound exits (the
hit_capflag replays identically); (C) the full exponential walk, unchanged, whenever the knapsack declines (insufficient trace precision, cutoff/decode failure, ANY failed trial division) — never an unvalidated factor. The Hensel lift target is raised to the plan'sk_needwhen the knapsack may engage (paper step 5, "additional Hensel lifting"; a larger modulus never changes the output — true factors' symmetric reps are already unique at2·B+1). lll_reduce(both arms) upgraded to the proper incremental H. Cohen Alg 2.6.3 form (the algorithm rc221's docstring already cited): ONE initial Gram–Schmidt, then μ/‖b*‖²maintained exactly across RED (already incremental) and SWAP (the exact-ℚ swap-update identitiesB′_{k−1} = B_k + μ²B_{k−1},μ′ = μB_{k−1}/B′_{k−1},B′_k = B_{k−1}B_k/B′_{k−1}+ the deep-row rotation). Byte-identical output (the maintained values equal the from-scratch recomputation at every step — verified per-step over a random corpus; the rc221 suite passes unchanged, including the degenerate-basisValueError, now raised at the vanishingB′pivot). Measured on the SD5 knapsack lattice (21×21): pure 34.5 s → 0.34 s, native 48.7 s → 0.40 s (the per-iteration O(m³) full recompute was the wall).- Same-rc 1:1 C peer (everything-mirrors):
srmech_factor_poly.cgains the full knapsack (vh_planpre-lift tower + modulus raise,vh_newton/vh_cut_rowtraces + cuts,vh_build,vh_lll_cutoffoversrmech_lll_reduce,vh_classes/vh_sort_blocksdecode,vh_replaythrough the SAMEfac_candidate/fac_peel,fac_walkphase ceilings) — all caller-arenasrmech_bigint(NO malloc, JPL Power-of-Ten clean, ≤ 60-line functions, ≥ 2 asserts, bounded loops, NULL-before-assert), byte-identical to the pure body (same plan, same lattice, same LLL, same decode, same replay).srmech_lll.cgains the incremental swap-update + a new exportedsrmech_lll_gso_normsq(the exact GSO ‖b‖² pairs the cutoff reads — additive symbol, *ABI stays 4**). Native lattice capVH_MAX_N = 32remaining factors (rows ≤ 40; above it the native path honestly runs the subset walk — the pure path has no cap);srmech_factor_squarefree_primitive_ws_boundgrows the vh block for deg ≥ 8 (the Cantor–Zassenhaus xorshift stream, the Hensel lift, and the mod-p factoring are UNTOUCHED — only the recombination phase changed). - Tests (
tests/test_vanhoeij_rc222.py): the byte-identity corpus — products of many small irreducibles, cyclotomics (x²⁴−1, x³⁰−1), multi-block Swinnerton-Dyer products (SD3a·SD3b, SD4·SD3, SD4·linears), a non-monic case, SD4, SD5 — each assertednative == pure == the pre-rc222 subset-only reference(the threshold pushed out of reach), plus known-shape pins and multiply-back; the SD5 stress asserts the KNAPSACK (not the walk) resolved it via the pure-arm_VH_STATSobservability counter AND that small-block inputs never engage it (phase A); TWO forced-fallback tests (LLL replaced by identity → cutoff rejects; traces zeroed → trial division rejects) assert the answer never changes; the subset-cap replay mirror assertshit_capsemantics are byte-identical at caps ¾/18 on a three-deg-8-block product; native/pure LLL identity on the knapsack lattice shape. Existing suites re-verified green: factor rc165 (39), LLL rc221 (26), eig_exact/jordan + qalg eig/vec (composing factor), pi_cascade + C-bignum transcendentals + QMat (shared bigint layer) — 299 collateral tests. Build gcc-13.4-O2 -DNDEBUGAND asserts-live-O1, zero warnings, no assert fires. - HONEST LIMITS: the ONE-SHOT lattice can decline — observed on x¹⁰⁵−1's post-phase-A remainder (24 factors, blocks {4,4,8,8}): spurious M-short vectors survive every trace depth provisioned (r = 7 kept rows vs 4 true blocks at s = 8..16 traces), so it falls back to the subset walk (~3 s wasted, output unchanged); the paper's iterative
L′-refinement is the known extension. The trace plan skips (→ subset walk) when the precision window can't cover even one trace within the lift-raise allowance (k_mig + k_mig/4 + e). The native lattice cap means a > 32-remaining-factor wall input runs the walk natively (the pure arm still knapsacks it). The knapsack itself adds no RNG — the Cantor–Zassenhaus stream stays byte-exact.
No new public op (the knapsack is an internal recombination path; srmech_lll_gso_normsq is a C-internal helper symbol with no ToolEntry) → tools.total stays 412, no registry regen, no rosetta ledger moves; all down-only ceilings unchanged. 5 SSOT files rc221 → rc222.
[0.9.0rc221]¶
Exact-ℚ LLL lattice-basis reduction — the van Hoeij knapsack foundation (ships as 0.9.0rc221). A new foundational primitive: the classic Lenstra–Lenstra–Lovász (1982) reduction of an integer lattice basis, in EXACT rational arithmetic (no float anywhere, no libm, no abs). This is the foundation for a future van Hoeij polynomial-factorization knapsack — the LLL recombination that supersedes the exponential Zassenhaus subset search in factor_integer_poly. Python op + a same-rc, byte-identical C peer (everything-mirrors; NO honest-split).
-
srmech.amsc.cascade.matrix_cascades.lll_reduce(basis, delta=(3, 4)): inputbasisisminteger row-vectors (lengthn, arbitrary-precision ints) spanning a rank-mlattice;delta = (num, den)is the Lovász parameter as an exact rational in (¼, 1]. Returns the LLL-reduced basis (minteger row-vectors): SAME lattice (a unimodular change of basis, det = ±1), size-reduced (|μ_{k,j}| ≤ 1/2forj < k), and Lovász-satisfying (‖b*_k‖² ≥ (δ − μ²_{k,k−1})·‖b*_{k−1}‖²), so the first vector is provably short. The engine is exact throughout: a Gram-matrix Gram–Schmidt orthogonalization over ℚ (μ,‖b*‖²as exactFraction/ arbitrary-precisionsrmech_bigintrationals in the C peer), size reduction by exact nearest-integer rounding ofμ(round(a/b) = floor((2a+b)/(2b))— never a float rint, neverabs: the|μ| ≤ 1/2guard is a Class-K sign branch on2·numvsden), and the Lovász swap decided on the exact ℚ inequality. Integer-in, integer-out; rotation-last-trivial (no projection). RaisesValueErroron a degenerate (linearly dependent) basis or adeltaoutside (¼, 1]. Class L (the lattice / Gram–Schmidt spectral content) ∘ Class K (the size-reduction sign pin-slots + the swap-sign boundary — never an ALUabs) ∘ Class N (the exact nearest-integer rational rounding) ∘ Class I (the ordered integer vector row operations). -
Same-rc 1:1 C peer (
c/src/srmech_lll.c,srmech_lll_reduce+_ws_bound+_entry_cap): the whole reduction over caller-arenasrmech_bigint(NO malloc, JPL Rule 3) — the μ/‖b*‖²GSO carried as num/den bigint rationals, the integer basis + size-reduction + Lovász swap all exact. The GSO is recomputed from the CURRENT integer basis each outer step (a pure function of the basis), so the whole computation is a pure function of(basis, δ)→ byte-identical to the Python pure body, which is the complete no-native fallback AND the parity oracle (both exact, same algorithm, same rounding). Native-dispatched via_native.lll_reduce_c; the C-sidemaxbitsmatches Python'sint.bit_lengthso the caller arena / out cap agree exactly (no under-sizing overflow). Any residual arena/out overflow returnsSRMECH_ERR_OVERFLOW(never a silent wrap) → the Python falls back to its byte-identical pure body. Additive symbols → ABI stays 4. -
Tests (
tests/test_lll_rc221.py), run on BOTH the native and forced-pure arms: KAT — the classic Wikipedia LLL example reduces EXACTLY to the published[[0,1,0],[1,0,1],[-1,0,2]], plus a 2-D example and an identity+target knapsack row → identity; the three exact properties verified against an independent Fraction GSO oracle — (1) size-reduced|μ_{k,j}| ≤ 1/2, (2) the Lovász condition on the output, (3) SAME lattice via the recovered unimodular transformU = (B·Aᵀ)(A·Aᵀ)⁻¹asserted INTEGER withdet(U) = ±1andU·A == B; native == pure BYTE-IDENTICAL over a deterministic-random integer-basis battery (small +10⁹-magnitude); δ validation, edge cases (m=0, m=1, ragged, degenerate), δ=1 termination, and determinism. Build verified gcc-13.4 both-O2 -DNDEBUGand asserts-live-O1(zero warnings under-Wall -Wextra; asserts do not fire on valid input). -
Registration: new
ToolEntry→tools.total411 → 412 (39 pinned count-test files swept;srmech_tool_registry.cREGENERATED, thetool_schema_sha256hash-ratchet re-locks); Rosetta ledger rowc_dispatched.CEIL_PYTHON_ONLY_DEBT/CEIL_BIGNUM_REFERENCE/CEIL_NON_COMPUTE_OWEDall stay 0 (the reduction dispatches through the Csrmech_bigintlayer — nobignum_referencerow). 5 SSOT files rc220 → rc221.
[0.9.0rc220]¶
Self-hosted coprime product for the cross-gcd-first Q multiply (#786 completion — closes the rc212 honest-note follow-up; ships as 0.9.0rc220). rc212 gave Q.__mul__ the full fractions.Fraction._mul discipline (Knuth TAOCP Vol 2 §4.5.1: cross-reduce BEFORE multiplying, then build the product pair directly via Q._from_coprime — no product-scale gcd at all), but its honest note flagged the one leg still off-substrate: the fast path's final raw product rode CPython int multiply, because _native exposed no raw bigint multiply — only the fused bigq_mul_c, whose built-in product-scale gcd-reduce is exactly the work Knuth's theorem eliminates. rc220 closes it, Python-only (no C source change — the wrapper rides the ALREADY-exported srmech_bigint_mul / rc168 Karatsuba srmech_bigint_mul_ws symbols; there is no dedicated C big-ℚ/Q-mul symbol to mirror — the big-ℚ multiply IS the _native.py ctypes composition over srmech_bigint_*, so everything-mirrors is already satisfied at the symbol level):
-
_native.bigint_mul_c(a, b)(new private helper,srmech/amsc/_native.py): the RAW signed product on srmech's caller-arena C bignum — marshal in,srmech_bigint_mul(/_wsKaratsuba when bound), marshal out; NO gcd, NO reduce.Noneon native-absent / arena overflow (CPythonintis the complete alternative, never an error path); counts intoBIGQ_DISPATCH_COUNT(the genuine-dispatch proof counter). -
Q._mulfast path →_coprime_product(srmech/amsc/q.py): the two proven-coprime products (a_num·b_num,a_den·b_den) dispatch tobigint_mul_cat/above the measuredrational._BIGQ_MIN_BITS(1024-bit) threshold, CPythonintbelow — the same size-adaptive gate as the rc167 #765 dispatch. This completes the self-hosting discipline for the whole cross-gcd multiply: the two cross gcds already rodesrmech_bigint_gcd(viacyclic.gcd); the product was the last CPython-int leg. -
BYTE-IDENTICAL, proven differentially:
tests/test_q_cross_gcd_mul_rc220.pypins new-Q.mul== the old multiply-then-reduce path (rational_mulon the raw operand pairs, the pre-rc212 body) ==fractions.Fractionacross random operands at 8→4096 bits (below/at/above both the 64-bit cross-reduce gate and the 1024-bit bigq threshold), signs, zeros, ±1, u64 boundary slivers, cross-cancelling reciprocalsa/b × b/a(→ exactly(1, 1)), chaineda/b × b/c, shared prime-power pairs, One-scale shared-factor operands — on the NATIVE arm AND the forced-pure arm (in-filemonkeypatch.setattr(_native, "HAS_NATIVE", False), the rc213/rc217 convention). Plus rc220-specific pins:bigint_mul_c == a·b(signs, zero, ±1, u64 boundary, 4096-bit), genuine-dispatch proof (BIGQ_DISPATCH_COUNTmoves on a big-ℚQmultiply), cleanNonedecline when native is absent, and the routing gate isrational._BIGQ_MIN_BITS. -
Measured (attested-to-measurement, Class B: WSL2 gcc 13.4 -O2, Python 3.10, best-of-rounds): this is the #765 self-hosting ARCHITECTURE move, not a raw-speed claim — and the honest numbers are: the raw-product LEG alone is marshal-overhead-dominated at the low band (0.14× / 0.36× / 0.83× vs CPython
intat 2048 / 4096 / 20000 bits — the product is cheap, so the fixed ctypes marshal + arena cost dominates where rc167's WHOLE-fused-op measurement was gcd-dominated), which lands the WHOLEQ.__mul__at 0.81× / 0.91× / 0.99× vs the rc212 CPython-product body on coprime random shapes — INSIDE the rc167 self-hosting acceptance band (0.59–1.24×, "≈cost-parity buys the architecture"). Cross-cancelling shapes are UNTOUCHED (the cancelled products sit below the 1024-bit gate and keep riding CPythonint). The rc212 Knuth win over the old multiply-then-reduce path is intact and grows with size: coprime 1.94× / 2.58× / 3.44×, reciprocal 1.44× / 1.89× / 4.90× at 2048 / 4096 / 20000 bits.
No C source change (the used symbols already exist; ABI stays 4), no new public callable (bigint_mul_c / _coprime_product are private _native/module helpers, peers of bigq_mul_c) → tools.total stays 411, no ToolEntry add, no registry regen, no rosetta ledger row moves, CEIL_PYTHON_ONLY_DEBT stays 0, CEIL_C_EXISTS_UNBOUND stays 0, CEIL_BIGNUM_REFERENCE stays 0, CEIL_NON_COMPUTE_OWED stays 0. Consumer suites re-verified green (Q cross-gcd rc211 / Q native-dispatch rc167 / Karatsuba+qpow rc168 / Q u64 parity rc7 / pi_cascade / C-bignum transcendentals rc35 / sqrt-isqrt cascade rc35 / numeric-protocol conformance). 5 SSOT files rc219 → rc220.
[0.9.0rc219]¶
Batched C peers for the encode-pipeline's other half (gh #827) — the RBS-LM context-window encode and the cached-eigenbasis spectral projection each collapse to ONE C crossing, with the parity KIND pinned per op; ships as 0.9.0rc219. The rc217 text peers closed the corpus→graph front; profiling the remaining encode pipeline showed the SAME shape twice more: at D=4096, k=16 ContextSubstrate.encode_context measured 127 ms/window with ~90%+ in Python per-token orchestration + k separate FFI hops, and spectral.decompose/recompose with the eigenbasis CACHED (the enwiki steady state) paid ~146 ms/state at n=128 — ~81 ms of Python descriptor-hash byte-building + ~75 ms of carrier→C-buffer marshalling around a µs-scale matvec. rc219 gives each its own batched C peer (4 additive symbols; ABI stays 4) and applies the rc218 macOS lesson as a build constraint: exact byte-identical parity where the leaves are integer/byte; within-tol/round-trip parity where the values are float-eig-derived — never a cross-platform SHA on float content.
-
srmech_rbs_lm_encode_word+srmech_rbs_lm_encode_context(c/src/srmech_rbs_lm.c) — EXACT byte-identical. The whole per-token loop — sha256 token seeds (Class A) → the CPython-replicating MT19937klein4_randommint → (F₂)² XOR bind (Class M) → strict per-bit majority bundle with the even-count odd-pad (never drop a real token) → sector bind — runs in ONE call, composing the existing public C leaves (srmech_sha256_hex/srmech_klein4_random/srmech_klein4_bind/srmech_klein4_bundle_accumulate/_resolve— thesrmech_klein4_composemodel). The C reproducestoken_seed's CPythoninit_by_arraykey exactly (hex-prefix value → little-endian uint32 words with the_seed_to_le_wordsbit-length word-count rule — leading-zero nibbles SHRINK the key, load-bearing for MT19937 parity) and the"__ctx_pos_{p}__"position labels. Profiling the collapsed call exposed the residual cost as the mints themselves (~85 µs each at D=4096; ~260/window byteglyph), and the byte vocab (seeds 0..255), byteglyph position keys (0x10000+i) and window position keys are WINDOW-INVARIANT — soencode_contexttakes an optional caller-owned mint cache (lazily filled, persistent across calls; a cached mint is byte-identical to a fresh one by construction; the C-side mirror of the pure path's per-instance_poskeydict). TheContextSubstrateowns one cache pair per instance;encode_word_byteglyph/encode_word_k4dispatch the single-word symbol. Measured (WSL gcc-13.4, byteglyph; rc218-path = the shipped per-token orchestration with native klein4 leaves, same machine): D=4096, k=16: 238.5 ms → 1.39 ms steady-state (~172×; first-window cold-cache ~23 ms; rolling-text realistic avg 1.7 ms); D=256, k=8: 12.5 ms → 0.06 ms (~216×) — the per-window latency that compounded to the multi-day full-enwiki estimate. -
srmech_spectral_decompose+srmech_spectral_recompose(c/src/srmech_spectral_codec.c) — WITHIN-TOL numeric. Over the ALREADY-CACHED eigenbasis (the peers deliberately do NOT re-implement the eig; themat_hermitian_eigendecomposeLRU stays),decomposecollapses carrier marshal +Vᴴ·state+ complex128 pack + Class-A content sha into one crossing, zero-copy from the eigenvectorMatbuffer, staging the exact conjugate-transpose bytes (conjugation = exact imag sign-flip) and dispatching the SAMEsrmech_dense_matmul_complexkernel the rc218mat_matvecroute uses — so same-machine byte-identity with the current native path holds BY CONSTRUCTION (asserted per-arm by the gate), while cross-platform/arm the eigenvectors diverge in the last ULPs, so the test contract is within-tol (≤1e-9) round-trip + same-machine kernel-equality, never a hardcoded cross-platform SHA (the rc218 macos-14 catch, now a standing constraint).recomposeis the dual (V·coeffs, the handle'scoefficients_bytesviewed directly as the complex128 operand). Separately (Python-only, NO C):_descriptor_hashis memoized by carrier identity × encoder_tag (a new dedicatedMatslot; the memo shares the documentedMat.buffermutation caveat) and a complexMat's flat buffer is recognised as ALREADY the canonical complex128 bytes (L.tobytes()replaces the per-element struct-pack loop — byte-identical, pinned by the rc218 hash-stability gate). Measured (n=128, cached eig, same machine): decompose 59.3 ms → 0.14 ms (~420×), recompose 60.7 ms → 0.11 ms (~550×) — memo + fast path + the one-crossing peer together. -
Ledger + registration. 4 rows move
composition_of_c→c_dispatched(srmech.rbs_lm.substrate.encode_word_k4/.encode_word_byteglyph/srmech.spectral.decompose/.recompose— each now dispatches its OWN srmech_* symbol);ContextSubstrate.encode_contextis a method (not a ledger row) and dispatches the context symbol. The non_compute splits are UNTOUCHED (these were compute rows) —test_non_compute_ratchet_rc170/ both annex ratchets hold as-is; all four ceilings HOLD (CEIL_PYTHON_ONLY_DEBT/CEIL_C_EXISTS_UNBOUND/CEIL_BIGNUM_REFERENCE/CEIL_NON_COMPUTE_OWED= 0/0/0/0). NO new public ToolEntry —tools.totalstays 411; ABI stays 4 (additive symbols only)._native.pybinds all four with explicit argtypes/restype (rc201 size_t discipline) + fourhas_native_*gates. -
Tests.
tests/test_rbs_lm_encode_context_rc219.py: native == pure == the pre-rc219 orchestration byte-for-byte across enc_modes / D / sectors / hex widths (incl. the odd-nibble 63), odd/even/empty windows (the pad path), empty + unicode tokens, mint-cache edges (tokens beyond the bytepos cache, windows beyond the ctxpos cache, cold+warm), a platform-independent pinned state hash (sound here — integer leaves only), genuine-dispatch ctypes spies, ledger-row asserts.tests/test_spectral_c_peer_rc219.py: same-machine kernel-equality vs the rc218mat_matvecroute (byte-for-byte, per arm), within-tol round-trip, descriptor fast-path + memo value-stability, repeat-call handle determinism, downstream delta/predict compose, dispatch spies, ledger-row asserts — and deliberately NO cross-platform coefficient SHA. Both files carry their forced-pure arm IN-FILE (monkeypatch.setattr(_native, "HAS_NATIVE", False)— the house rc213/rc217 convention, gating off the rc219 peers AND the klein4/carrier leaves in one move), so native == fully-pure is asserted in a single run. ASAN/UBSAN clean on the sanitized asserts-live build. JPL-clean (caller-arena, ≤60-line fns, ≥2 asserts, no goto/malloc/abs);-Werrorclean in Release (-DNDEBUG) and asserts-live modes.
5 SSOT files rc218 → rc219.
[0.9.0rc218]¶
The parity-completeness closure (task #826, surfaced by the rc217 #1360 sweep) — the LAST 4 untracked Python-only module families join the rosetta ledger roots, and the "no untracked Python-only compute" gap is CLOSED; ships as 0.9.0rc218. The rc217 package-wide parity sweep found four module families outside the ledger walk (srmech.spectral / srmech.rbs_lm / srmech.introspect / srmech.profile_loader) plus one confirmed discipline breach (direct hashlib.sha256 in spectral). rc218 brings all four under the walk, classifies every op, routes the spectral hand-rolled kernels through their existing C-backed ops, and documents the one deliberate exclusion (adapters) — NO new C symbol needed: every compute leaf reached is already C-backed. All four ceilings HOLD at 0 (CEIL_PYTHON_ONLY_DEBT / CEIL_C_EXISTS_UNBOUND / CEIL_BIGNUM_REFERENCE / CEIL_NON_COMPUTE_OWED).
-
The 4 roots join ALL FOUR walk sites (
test_rosetta_completeness._ROOTS/conftest._ROSETTA_ROOTS/test_rosetta_transitive_standalone._ROOTS/notes/_rosetta_inventory.ROOTS— the inventory script also catches up to the rc177/rc183 bus/dsl/mcp/cli/llm extensions, so all four walks are now IDENTICAL at 12 roots). +30 ledger rows (596 → 626): spectral (8) — the 7 compute ops (decompose/recompose/predict/delta/similarity/prediction_error/truncate_sparse) arecomposition_of_cover the C-backedmat_hermitian_eigendecompose/mat_matvec/hdc.bind/hdc.hamming/hdc.similarity/rational.cexp/sha256_bytes;clear_eigenbasis_cacheis non_compute/dev_tooling (test-isolation reset). rbs_lm (8) — the substrate encode helpers (token_seed/encode_word_k4/encode_word_byteglyph/encode_bigram_l1/encode_skeleton_l2/encode_sentence_l3/sim_k4_batch/scale_signature) arecomposition_of_creaching the C-backedklein4_*/sha256_bytesleaves (verified non-zero transitive reach — no zero-reach pin needed). introspect (11) — 6 host_glue (publish/list/by_pid/_maybe_auto_publish/_writer.introspect_dir/_writer.emit_if_publishing—~/.srmechFS I/O), 5 composes_c (describereachesget_tool_schema;native_status+ the 3_eventwire-format accessors are justified ZERO-REACH rows, deliberately added toCOMPOSES_C_ZERO_REACH_PINNED). profile_loader (3) — dev_tooling (the Python entry-point plugin mechanism is a host-Python affordance), added toNON_COMPUTE_DEV_TOOLING_EXEMPTalongsidespectral.clear_eigenbasis_cache. -
The 3 spectral micro-refactors — hand-rolled kernels → existing C-backed ops, gated by refactor value-preservation. The
Vᴴ·stateprojection indecomposeand theV·coeffsreconstruction inrecomposenow route throughlaplacian.mat_matvec(the rc148 dct precedent); theprediction_errorpopcount-density gate now routes throughhdc.hamming(raw_delta, 0⃗)(exact integer — value-identical by construction). Development-time verification captured the full rc217 handle baseline at 207d182e before the refactor and re-captured after:diffempty on the Linux/gcc-13.4 native AND forced-pure arms (coefficient bytes, predict/truncate SHAs, exact recompose values, delta/gated-error bytes) — the refactor changed nothing. The shipped gate (tests/test_spectral_hash_stability_rc218.py) pins each invariant at the granularity it actually holds: the input-byte-derived hashes (the substrate descriptor hash + the_sha256_hexClass-A site — the values downstream consumers key on, and where thehashlib→sha256_bytesfix lives) are pinned byte-identical on every platform/arm; theprediction_errorgate identities are pinned exactly (integer XOR/popcount); and the float-eig-derived coefficients/recompose are pinned within-tol, live and per-platform (decomposecoeffs == the sesquilinearVᴴ·statethey replaced, andrecompose∘decomposeround-trips to the input). The float-eig content is deliberately NOT pinned to a cross-platform byte-SHA — the numeric Hermitian-Jacobi eigenvectors differ in the last ULPs across libm/FMA (a pre-existing rc217 property, the same reason the native and pure arms differ in eigenBASIS), so a single-platform SHA baseline is unsound cross-platform (caught on macos-14 CI, fixed before merge). -
The
hashlibdiscipline fix (the confirmed rc217-sweep breach).spectral._sha256_hexand the incrementalh.update()chain inspectral._descriptor_hashnow route throughsrmech.amsc.format.sha256_bytes(_descriptor_hashrewritten as ONEsha256_bytescall over the same concatenated canonical bytes — value-identical, verified in the same gate). No directhashlib.sha256remains insrmech/spectral/. -
The adapters IO-exclusion, documented.
ROSETTA_LEDGER.mdgains the "adapters IO-exclusion" section:srmech.amsc.adaptersis the collector surface (network/file IO viarequests+ optionalnetCDF4/rasterioparsers) — collection happens once on a host Python; what a bare-C host/MCU consumes is the already-attested NDJSON, which IS C-mirrored (srmech_ndjson_iter/srmech_json/srmech_toml). Each of the four walk skip sites carries a one-line comment naming the exclusion. -
Re-pinned ratchets.
test_non_compute_ratchet_rc170._EXPECTED_SPLITcomposes_c 115 → 120 / host_glue 15 → 21 / dev_tooling 44 → 48,_TOTAL_NON_COMPUTE174 → 189;test_annex_ratchet_rc177._FULL_SPLITre-pinned to the same. NO new public ToolEntry —tools.totalstays 411; ABI stays 4 (no C change at all).
5 SSOT files rc217 → rc218.
[0.9.0rc217]¶
C peers for srmech.amsc.text (gh #1360) — the §40/§52 text→graph ingestion hot loop goes native, and the ledger mis-classification that hid it is closed; ships as 0.9.0rc217. The three text ops (tokenize / cooccurrence_edges / cooccurrence_topk — the K1 chain's text → tokenize → cooccurrence_edges → dense_laplacian front + the §52 streaming encode) shipped rc50/§52 as pure-Python kernels with NO C symbol to dispatch to, so the full-enwiki comprehended-encode ran its dominant cost in Python even on a native wheel (a measured multi-DAY estimate). THE GAP was a ledger mis-classification, not a missing root: srmech.amsc.text was always walked (the srmech.amsc root covers it), but its three rows sat in non_compute/composes_c — and the composes_c transitive-reachability guard only fires on rows that REACH a not-ready ledger leaf, so a SELF-CONTAINED pure-Python compute kernel (calls no srmech op → reaches nothing) passes it silently while CEIL_PYTHON_ONLY_DEBT reads 0. rc217 builds the byte-identical C peers, moves the rows to c_dispatched, and pins the hiding spot shut.
-
The C peers (
c/src/srmech_text.c; 4 additive symbols, ABI stays 4):srmech_text_tokenize,srmech_text_cooccurrence_edges,srmech_text_cooccurrence_topk(+ its read-out siblingsrmech_text_cooccurrence_topk_extract). BYTE-IDENTICAL parity is the correctness gate (token stream, integer pair counts,(-weight, index)tie-breaks, first-seen edge weights, lexicographic edge order — or the downstream Laplacian differs between hosts). The corpus-linear hot loops run fully in C — the per-codepoint tokenize walk (run segmentation / per-codepoint casefold / end-apostrophe trim / min-len / sorted-stoplist binary search, with an ASCII fold LUT fast path), the windowed pair-count accumulation (splitmix64 open-addressed hash in a caller arena, load ≤ ½,SRMECH_ERR_OVERFLOW→ grow + retry), the §52 bounded chunk flush (directed-record heapsort → per-node two-pointer merge → cap-truncation by(-w, nbr)via the~wascending transform — a Class-K reorder, neverabs()), and the final per-node top-K + first-seen-deduplicated edge read-out. Deterministic in-house record heapsorts (unique total-order keys → one result on every platform; no libcqsort). The vocab-scale string↔id mapping stays Python (thesrmech_klein4_cooccurrence_foldsplit precedent). Malloc-free caller-arena, JPL-clean (≤60-line fns, ≥2 asserts, no goto/malloc/abs),-Werrorclean both modes, ASAN/UBSAN clean across the full parity battery + 650 fuzz trials. -
The Unicode-parity architecture: caller-provided tables, byte-identical BY CONSTRUCTION on any interpreter. The tokenizer's tables — the kept-bitset (bit cp set iff
unicodedata.category(cp)[0] ∈ {L, M}) and the casefold exception table (~1.5k non-identity rows as sorted codepoints + offset-indexed folded-UTF-8 blob) — are built ONCE per process (~0.5–1 s, lazy) from the RUNNING interpreter'sunicodedataand handed to C, so native == pure holds on any Python / Unicode version with NO vendored Unicode data to drift. A bare-C host supplies its own tables (inputs, like the stoplist). Safety properties verified at build time (no fold output contains an apostrophe → the C trim-after-fold order equals the pure strip-before-fold; violation would decline the native path entirely) and locked by test (str.casefold per-codepoint concat property). Lone-surrogate text (UTF-8-unencodable) declines to the complete pure body — inform-don't-limit. -
Measured hot-loop speedup (WSL gcc-13.4 build, 5.4 KB wiki-shaped article / 200-doc corpus):
tokenizepure 1.38 ms → native 0.19 ms (7.1×);cooccurrence_edges116.9 ms → 31.8 ms (3.7×);cooccurrence_topk142.0 ms → 31.8 ms (4.5×); the end-to-end tokenize+topk encode front 120.9 ms → 20.1 ms (6.0×). The residual native-path floor is the SHARED Python vocab-scale string↔id mapping (both paths pay it); an id-emitting tokenize→cooccurrence fusion is the named next lever if the encode needs more. -
The ledger fix + the NEW recurrence guard. The 3 rows move
non_compute/composes_c→c_dispatched(CEIL_PYTHON_ONLY_DEBTstays 0 — they never touch the debt bucket; non_compute total 177 → 174, composes_c 118 → 115, re-pinned intest_non_compute_ratchet_rc170.py+ both annex ratchets). NEW pinCOMPOSES_C_ZERO_REACH_PINNED(test_rosetta_completeness.py::test_composes_c_zero_reach_rows_are_pinned): the exact set of composes_c rows whose transitive walk reaches ZERO ledger ops (60 rows — all justified accessors / constructors / validators / arg-grammar) is pinned in both directions, so a NEW zero-reach composes_c row must be DELIBERATELY allowlisted — a self-contained compute kernel can never again hide in composes_c unnoticed (the exact question text.py never got asked at classification time). -
The package-wide parity sweep (the user-directed integrity close). Enumerated EVERY publicly-reachable
srmech.*callable against the ledger + thenm -DC-symbol table. Inside the 8 ledger roots: NO other unclassified op (the live-surface == ledger asserts hold). OUTSIDE the roots, four module families are honestly UNTRACKED (reported for orchestration as future annex rcs, the rc177 bus/dsl + rc183 mcp/cli/llm pattern — NOT folded into this rc):srmech.spectral(8 ops — the v0.4.2 decompose/delta/recompose layer; composes the C-backed Hermitian eig + hdc, plus a direct-hashlibcache key that should route throughsha256_bytes),srmech.rbs_lm(8 substrate encode ops composing C-backed klein4),srmech.introspect(11, host-glue FS/status incl. the top-levelsrmech.describe/native_statusre-exports),srmech.profile_loader(3, dev-tooling plugin loader) — plus the long-standing deliberatesrmech.amsc.adaptersexclusion (19 fetch/parse IO ops, host-glue-shaped, excluded from the walk since rc7). -
Registration. NO new public op —
tools.totalstays 411; the three ToolEntries are unchanged (summaries already op-accurate; the stale "Both pure-Python" code comment updated), so the rc184/rc205 tool/carrier registry hash-ratchets are untouched._native.pybinds all four symbols with explicit argtypes/restype (the rc201 size_t discipline) + threehas_native_text_*gates. New test suitetests/test_text_c_rc217.py: byte-identical native == forced-pure across the Unicode battery (NFC/NFD, Cyrillic/CJK, ß→ss min-len, ligature folds, curly apostrophes, combining marks), window boundaries + document-boundary reset, vocab/vocab_size modes, top-K ties + truncation + chunk-cadence parity (includingchunk_docs=1), first-seen edge weights, generator single-pass streaming, the fold-concat + no-apostrophe-fold properties, ledger-row + registration + genuine-native-dispatch (ctypes spy) asserts.
5 SSOT files rc216 → rc217.
[0.9.0rc216]¶
Public cn_vwp_multisum_lhs op (elliptic reduction-rows dive item #688) — the symbolic Cₙ very-well-poised (VWP) elliptic multisum LHS builder promoted first-class, WITH a same-rc 1:1 C peer; ships as 0.9.0rc216. The LEFT-hand side of the Cₙ elliptic Jackson summation — the n-fold Cₙ VWP sum over the partitions Λ_{nN} = {N ≥ λ₁ ≥ … ≥ λₙ ≥ 0} built SYMBOLICALLY as an exact ThetaSum — has existed since rc96 as the PRIVATE _cn_lhs_thetasum (born the rc96 test oracle's symbolic twin, promoted to the rc101 per-call symbolic-verify engine), reachable only through multivariate_elliptic_jackson(verify=True). It is now a first-class public op, so BOTH sides of the Rosengren Theorem 2.1 identity are first-class: cn_vwp_multisum_lhs (the LHS sum) and multivariate_elliptic_jackson (the RHS closed form), with (LHS − RHS).is_zero — the rc101 proof — composable by any consumer.
-
The op:
srmech.amsc.elliptic_jackson.cn_vwp_multisum_lhs(a, b, c, d, x, q, N, n) → ThetaSum. Per partition: the diagonalθ(a·x^{2(1-i)}q^{2λᵢ})/θ(a·x^{2(1-i)})quotients with the monomial prefactor∏ᵢ q^{λᵢ}x^{2(i-1)λᵢ}(sign in the Class-KEllMonomialcoeff branch, neverabs()); the off-diagonal (i<j) root-system coupling quartetθ(x^{j-i}q^{λᵢ-λⱼ})/θ(x^{j-i}) · θ(a·x^{2-i-j}q^{λᵢ+λⱼ})/θ(a·x^{2-i-j}) · (a·x^{3-i-j};q)_{λᵢ+λⱼ}(x^{j-i+1};q)_{λᵢ-λⱼ} / ((aq·x^{1-i-j};q)_{λᵢ+λⱼ}(q·x^{j-i-1};q)_{λᵢ-λⱼ}); and the six num / six den VECTOR theta-Pochhammer bases(a·x^{1-n}, b, c, d, e, q^{-N}; q, x)_λ / (q·x^{n-1}, aq/b, aq/c, aq/d, aq/e, a·q^{N+1}; q, x)_λ, withefixed by the balancingbcde·x^{n-1} = a²q^{N+1}. Each summand is anEllRatio; theC(N+n, n)partitions sum into one exactThetaSum. The public wrapper validates (TypeError on non-EllMonomial, ValueError onN < 1/n < 1) and keeps_cn_lhs_thetasumas the implementation — the promotion is value-identical to the oracle by construction, and pinned so by test. -
Same-rc 1:1 C peer
srmech_cn_vwp_multisum_lhs(+_ws_bound),c/src/srmech_cn_vwp_multisum_lhs.c— everything-mirrors, NO honest-split. A C-MIRROR PARITY build on the rc95srmech_elliptic_partial_fractionmulti-term wire form (there is NO ThetaSum-CONSTRUCTION C surface, so the peer emits the per-partition EllRatio TERMS as a row stream and the Python side sums them viaThetaSum.from_ellratio++, identically to the pure path) with the rc96srmech_multivariate_elliptic_jacksonparameter head (the 6 monomials + N + n, PLUSn_terms = C(N+n, n)computed by the caller and count-checked by the kernel — a mismatch returnsSRMECH_ERR_BAD_INPUT). The kernel enumerates the partitions by a lexicographic odometer (the exact order the Python oracle's filtereditertools.productyields), builds each summand from precomputedq^i/x^k/x^{-k}power ladders + the 12 VWP bases + the balancingeover the sharedsrmech_ellbase_*exact-ℚ monomial algebra, and runser_build(theEllRatio.__init__mirror) per term — byte-exact to the Python carrier. Per-term working memory is cursor-saved/restored (one term's working set, reused). The nativeThetaSumis trusted ONLY after it==the pureThetaSum(the complete alternative + the parity oracle). Malloc-free caller-arena (JPL Rule 3, no compiled-in cap), JPL-clean,-Werrorclean both modes. ABI stays 4 (two additive symbols). -
MPM attestation (extracted-PDF verification at build, NOT training-data trust). The keystone: Hjalmar Rosengren, "A proof of a multivariable elliptic summation formula conjectured by Warnaar", arXiv:math/0101073v1 [math.CA] (9 Jan 2001), Theorem 2.1, Eq. (5) — author + title + arXiv id + theorem verified from the extracted PDF (sha256
be4a18685749cf05a358cf4b56170ac929940eb0d100ea550d72b1b1cab6fee9); the Eq. 5 LHS (diagonal E-quotients +q^{λᵢ}x^{2(i-1)λᵢ}prefactor, the (i<j) coupling quartet, the six-base VWP vector Pochhammer quotient, the sum rangeΛ_{nN}, the balancingbcde·x^{n-1} = a²q^{N+1}) matches the builder term-for-term. NOTE the exact title: the paper is the proof of Warnaar's conjecture — the module's earlier shorthand cite ("A multivariable elliptic summation formula") is superseded by the exact verified title in the new op's docstring + module reference. -
Registration + ratchets. New ToolEntry
srmech.amsc.elliptic_jackson.cn_vwp_multisum_lhs→tools.total410 → 411 (38 pinned count-test files / 50== 410sites updated, + thetest_carrier_schema_rc205pinned-count test renamed_is_411);c/src/srmech_tool_registry.cREGENERATED viagen_tool_registry.py(the C tool-schema byte-identity hash-ratchet re-locks: sha256(C canonical JSON) == the Pythontool_schema_sha256) andc/src/srmech_carrier_registry.cREGENERATED viagen_carrier_registry.py(the rc205 carrier schema bakes a carrier→ops back-index over the tool list); Rosetta ledger rowsrmech.amsc.elliptic_jackson.cn_vwp_multisum_lhs→c_dispatched.CEIL_PYTHON_ONLY_DEBTstays 0,CEIL_BIGNUM_REFERENCEstays 0,CEIL_NON_COMPUTE_OWEDstays 0. New test suitetests/test_cn_vwp_multisum_lhs_rc216.py: promotion == the private oracle (value-identity to_cn_lhs_thetasumacross the feasible small-(n, N) battery), the Thm 2.1 identity through the PUBLIC ops ((cn_vwp_multisum_lhs − from_ellratio(multivariate_elliptic_jackson)).is_zerois True on the rc101 feasible set — and False against a perturbed closed form, the discrimination guard), native == forced-pure parity (term-count, exactThetaSumequality), the n=1 Frenkel–Turaev shape sanity, type/validation contracts, registration (ToolEntry; tools.total == 411; Rosetta row;__all__).
5 SSOT files rc215 → rc216.
[0.9.0rc215]¶
Public winding_fold op (the #741 mod-should-be-divmod audit, finding F-2) — the exact Machin-2π seam-fold theta → (w, theta_res) exposed first-class; ships as 0.9.0rc215. The fold theta = 2π·w + theta_res (w = round(theta/2π) round-half-toward-+∞ = the METACYCLE winding; theta_res the EPICYCLE residue, |theta_res| ≤ π) has existed since rc207/gh#1276 — natively as srmech_winding_fold (the Q61 2/π quarter-turn machinery srmech_cos/srmech_sin fold with) and pure as the laplacian _eph_seam_fold (the exact-rational Machin-2π divmod) — but was reachable ONLY through propagate_wound. An external consumer with an accumulated angle (a Kuramoto phase, an Im(z)·λ from its own solve) had to hand-roll a float theta % (2*pi) — BOTH the grading-collapse the divmod audit hunts AND a precision hazard vs the exact fold.
-
The op:
srmech.amsc.cascade.winding_fold(theta) → (w, theta_res)(defined atcascade/one.py— the gh#1276 winding surface, where thesrmech_winding_*peers dispatch and wherewfeeds the One's metacycle dial:the_one(σ, θ_num, θ_den, w=(w,0,0))→sigma_effective/spinor_sign/unwrapped_phase). Cascade decomposition: Class-I divmod (quotient RETAINED) over the exact Class-N 2π constant; residue sign Class K/C (explicit branch, neverabs()). Dispatches to the EXISTING nativesrmech_winding_fold(rc207; no new C symbol — the C peer already ships, its_native.py._bind()argtypes/restype were already declared per the rc201 discipline) inside its|theta| < 2^55domain; a non-OK native status (the honestsrmech_cos-family domain bound) falls to the COMPLETE pure alternative — the SAME_eph_seam_foldMachin-2π divmodpropagate_wound's pure path runs (arbitrary-precision, any finite float; no forked 2π constant anywhere). Native == pure:wexact-integer equal;theta_resequal to the fold grids' common resolution (Q61 native / 2⁻⁴⁴ pure — both quantise the SAME real residue; the rc207 parity contract). Contract boundaries guarded loudly: complextheta→TypeError(a real-axis fold); non-finite →ValueError(finite-angle domain). -
Registration + ratchets. New ToolEntry
srmech.amsc.cascade.winding_fold→tools.total409 → 410 (38 pinned count-test files / 49== 410sites updated, + thetest_carrier_schema_rc205pinned-count test renamed_is_410);c/src/srmech_tool_registry.cREGENERATED viagen_tool_registry.py(the C tool-schema byte-identity hash-ratchet re-locks: sha256(C canonical JSON) == the Pythontool_schema_sha256) andc/src/srmech_carrier_registry.cREGENERATED viagen_carrier_registry.py(the rc205 carrier schema bakes a carrier→ops back-index over the tool list, so a new ToolEntry shifts it — its own byte-identity + codegen-idempotence ratchets re-lock); Rosetta ledger rowsrmech.amsc.cascade.winding_fold→c_dispatched(the C symbol exists and is dispatched); the dotted defining namecascade.one.winding_foldjoins thetest_tool_schema_coverageflat-name exemption list (theone.the_oneprecedent).CEIL_PYTHON_ONLY_DEBTstays 0,CEIL_BIGNUM_REFERENCEstays 0,CEIL_NON_COMPUTE_OWEDstays 0. No C source change; ABI stays 4. New test suitetests/test_winding_fold_rc215.py: the angle battery (0, ±π, ±(2π+ε), large multiples of 2π, negatives, sub-seam small angles), the lossless2π·w + theta_resround-trip against the module's own Machin-2π (Fraction-exact, no forked constant), retrograde antisymmetry (Class-C negation of both harvests), the ≥2⁵⁵ native-boundary fallback (exact at any finite float via the pure fold, Fraction oracle), native == forced-pure parity, the propagate_wound cross-check (same fold, same verdicts per mode), the One-readout reuse contract, type/registration contracts. -
⚠ R-1 (the divmod-audit residue riding this rc) — a REAL but LATENT bug SURFACED and RECORDED as a strict-xfail (task #824), NOT papered over. The divmod audit's R-1 residue asked whether
riemann_theta.transform's 8th-root multiplier exponentkexpCOMPOSES. It does not: the compose-two-Sp-transforms-vs-direct-product check FAILS at ALL THREE genera (g2 Sp(4)/g3 Sp(6)/g4 Sp(8), pure path) — the composed-vs-direct defect(kexp(γ₂γ₁, m) − kexp(γ₁, m) − kexp(γ₂, γ₁·m)) mod 8takes BOTH values {0, 4} varying WITH the characteristic m for many generator pairs (g2 31/81 incl. plainUrot·T11; g3 8/25; g4 8/25). Any consistent transformation conventionθ[γ·m] = ζ₈^kexp·κ₀(γ)·θ[m]forces a characteristic-INDEPENDENT defect (the κ₀/Maslov cocycle is γ-only), so the shipped(reduced characteristic, kexp)pair is inconsistent — the mod-2 OUTPUT foldRiemannTheta(new_epp % 2, …)drops the characteristic-shift sign θ[ε+2δ] = ±θ[ε] (a ζ₈⁴ = −1) that belongs INSIDE kexp; simple single-term fold-sign corrections do not repair it (the correct reduced-characteristic convention needs a genuine Igusa §V.1 derivation + lockstepsrmech_riemann_theta_sp{4,6,8}_charC-peer changes). VERDICT: LATENT — NO shipped result is affected. No operational (non-test) consumer composes two transforms: only a_native.pycomment references.transform, and the shipped Göpel/Rosenhain g2–g4 suites test SINGLE transforms, not compositions.transform's per-step integer characteristic action and single-γ kexp values are correct; the rc73/rc85 gates still hold. Recorded in-repo astest_riemann_theta_rc73.py::test_kexp_composes_consistently_all_genera— a@pytest.mark.xfail(strict=True)gate asserting the CORRECT (characteristic-independent-defect) behaviour across g2/g3/g4, with the full per-characteristic Urot·T11 witness table in the test comment.strict=Trueflips it to a FAILURE the moment #824 folds the shift sign into kexp — the natural close signal. Tracked as task #824.
5 SSOT files rc214 → rc215.
[0.9.0rc214]¶
jpeg numeric-DCT C peer (#753) — closed_form_ops.jpeg moves composition_of_c → c_dispatched over TWO new C symbols srmech_jpeg_encode_f64 / srmech_jpeg_decode_f64 (+ srmech_jpeg_ws_bound); ships as 0.9.0rc214. jpeg is the float-DCT NUMERIC op DEFERRED at rc144/B6b out of the exact coder batch (its sibling Huffman went c_dispatched there); rc155 reached C only as a composition — each 8×8 block's DCT riding dct.op → mat_matvec ∘ mat_matmul → srmech_dense_matmul_complex, i.e. 4 Python-glue dispatches PER BLOCK, rebuilding the Class-N cosine basis per call. It now has its dedicated blocked pipeline kernel: ONE ctypes crossing for the whole image.
-
The C peer (
c/src/srmech_jpeg.c). Per bs×bs block: strided block extract → separable 2-D DCT-IIZ = (2·B₂·X)·(2·B₂ᵀ)(cols then rows, the exact loop shape of the puredct.opaxis-0/axis-1 cascade) → Class-K round-half-even quantiseround(Z ⊘ QT)(encode); dequantiseQ ⊙ QT→ 2-D DCT-III with the weight-1j == 0correction →1/(2·bs)²normalise (decode). The round-half-to-even quantiser is the exact C twin of Pythonround()— integer-truncate + tie-to-even sign branches, no libmrint(), nofabs()/abs()(Class-K pin-slot). The cosine bases B₂/B₃ + quant table are CALLER inputs (the Python side builds them ONCE through the byte-exact Class-Nrational.coscascade — the SAME basis the pure path uses, so no basis-derivation drift; a bare-C host builds them from the shipped libm-freesrmech_cos) — the rc149iirtaps precedent. All scratch bump-carved from the CALLER arena (srmech_jpeg_ws_bound(bs)= 2·bs² doubles; JPL Rule 3, no malloc; under-sized →SRMECH_ERR_OVERFLOW); a quantise quotient at/past 2⁶² returnsSRMECH_ERR_OVERFLOW(OVERFLOW-not-wrap); zero qt entries →SRMECH_ERR_BAD_INPUT. JPL-clean (flat bounded loops, ≤60-line functions, ≥2 asserts each,srmech_status_treturns,-Werror-clean both modes). -
WHY a new symbol (the rc149 honest-classification test). The pipeline is BLOCKED + FUSED — strided extraction, two basis multiplies, and the elementwise quantise per block; routed through the generic dense matmul it re-crosses the ctypes boundary O(bh·bw) times and rebuilds the basis per call. The blocked pipeline over caller buffers is the minimal genuinely-new numeric kernel (same shape of argument as
srmech_iir_lfilter_f64's sequential recursion). -
NUMERIC (WITHIN-TOL native == pure, NOT byte-identical — the F1-FFT / F2-SVD / B4 contract). Float DCT: the stage accumulations may FMA-fuse ~1 ULP on some platforms (macOS clang), so the parity contract is differential — the reconstructed image to reldiff ≤ 1e-9; the quantised ENCODE coefficients are integers and are asserted exactly equal on fixtures pre-checked ≥ 1e-6 away from round-half-even boundaries (a boundary fixture fails the precondition, never flakes the parity). New parity + oracle suite
tests/test_jpeg_dct_c_rc213.py(constant-image DC-only oracle DC = 4·bs²·v, high-quality round-trip MSE, whole-block truncation, encode/decode/roundtrip native == forced-pure, explicit quant-table + block_size=4 path, clean-decline-when-absent). -
Dispatch + ledger.
jpeg.opdispatches encode and decode through_native.jpeg_{encode,decode}_f64_c(argtypes/restype declared in_native.py._bind(), hasattr-guarded) and falls back to the complete numpy-free pure block-DCT cascade otherwise. Rosetta rowclosed_form_ops.jpeg.opcomposition_of_c → c_dispatched. No new public callable →tools.totalstays 409, no ToolEntry add, no registry regen;CEIL_PYTHON_ONLY_DEBTstays 0,CEIL_BIGNUM_REFERENCEstays 0. ABI stays 4 (new symbols are additive). 6 SSOT files rc209 → rc213.
[0.9.0rc213]¶
The qm CONSTANT matrices realizable in C — srmech_qm_* emitters + the -0.0 canonicalization (closes task #755; ships as 0.9.0rc213). The base qm constant matrices — the Pauli σ_x/σ_y/σ_z + I₂ (spin.py), the Dirac γ⁰..γ³ + the Minkowski metric η (relativistic.py), and the eight Gell-Mann λ¹..λ⁸ + the SU(2)/SU(3) structure constants (gauge.py) — were Python LITERALS with NO C source: classified composition_of_c, yet a bare-C host could not produce the constant DATA (a real python-free gap the rc145 "no new C symbol" decision left open). Two coupled parts:
-
Zero canonicalization (
-0.0 → +0.0, true zeros only). The literals carried-0.0in mathematically-zero slots —-1jnegates BOTH components (σ_y[0,1].re,λ²/λ⁵/λ⁷[i,j].re), and_scale(-1.0, ·)leaves-0.0across the negated γ blocks (6 slots per γ: the zero entries of the negated block + the imaginary parts of its±1entries). Every flipped slot is a TRUE zero (the exactly-zero component of a0 / ±1 / ±ientry — never a signed zero the math depends on); the pure γ assembly now passes_canon_true_zeros(x + 0.0flips ONLY-0.0), andσ_y/λuse the canonicalcomplex(0.0, -1.0). Byte-verified: all 25 derived-op outputs (γ₅, Weyl projectors, charge conjugation, Clifford/Pauli residuals exact-zero, Dirac operator, spin operators, Casimirs, Lie residuals, holonomies, CHSH) are byte-identical before/after — only the constants themselves (and their direct 0.5-scalingssu2/su3_generators) flip true-zero sign bits. -
Standalone-C constant emitters, dispatched (everything-mirrors).
c/src/srmech_qm_constants.caddssrmech_qm_pauli(which = 0..3: σ_x/σ_y/σ_z/I₂; 2×2 interleaved),srmech_qm_dirac_gamma(μ = 0..3; 4×4 interleaved, Dirac basis),srmech_qm_minkowski_metric(4×4 REAL),srmech_qm_gell_mann(a = 1..8; 3×3 interleaved; the λ⁸1/√3via the shipped libm-freesrmech_rational_sqrt— byte-identical to Python's1.0/float(rational.sqrt(3.0)), one rounding + exact power-of-two scaling each path),srmech_qm_su2_structure(ε^{abc}, 27 doubles) andsrmech_qm_su3_structure(f^{abc}, 512 doubles;f^{458}=f^{678}=√3/2via the same sqrt cascade; same seed/permutation/sign-multiply order as the pure fill). The Python constant ops DISPATCH to them when native is present; the canonical pure literals remain the complete, byte-identical alternative (no float tolerance — pinned bytests/test_qm_constants_c_rc212.py, incl. a no--0.0-anywhere sweep on BOTH paths + independent Peskin-Schroeder/Gell-Mann value oracles). -
HONEST SCOPE — what did NOT move. The so8/triality bases are DERIVATIONS, not literals (the octonion mult table composes the c_dispatched
srmech_cd_basis_product; the triality companion maps' exact-ℚ solve is reproducible viasrmech_qmat_rref) — they staycomposition_of_c, as dosu2_generators/su3_generators(0.5-scalings of the now-C-emitted bases) and all rc145 derived ops.
LEDGER: 7 rows move composition_of_c → c_dispatched (pauli_matrices, pauli_identity, gamma_matrices, minkowski_metric, su3_gell_mann_matrices, su2_structure_constants, su3_structure_constants). Neither bucket is ceilinged — CEIL_NON_COMPUTE_OWED stays 0, CEIL_BIGNUM_REFERENCE stays 0. The emitters are INTERNAL data producers feeding the existing public constant ops — no new public callable, tools.total stays 409, no registry regen. ABI stays 4 (six additive symbols, no callback typedef). ⚠ Every new ctypes binding carries explicit .argtypes/.restype in _native.py._bind() (the rc201 ABI-UB lesson). JPL-clean (caller buffers, no malloc/goto/abs, ≤60-line fns, ≥2 asserts); strict -std=c11 -Werror -Wpedantic -Wextra green in both -O2 and -DNDEBUG; ASAN/UBSAN clean.
[0.9.0rc212]¶
Cross-gcd-first Q multiply (#786, Knuth TAOCP Vol 2 §4.5.1 — the full fractions.Fraction._mul discipline at the carrier level; ships as 0.9.0rc212). The exact-ℚ scalar carrier Q computed a/b × c/d by multiplying-then-reducing ((a·c)/(b·d) fed to one product-scale gcd — and then Q.__init__'s _reduce_rational re-reduced the already-reduced result, a second product-scale gcd probe), so intermediates blew up to PRODUCT scale. CPython's Fraction instead cross-reduces BEFORE multiplying: g1 = gcd(|a|, d), g2 = gcd(|c|, b), result = (a/g1 · c/g2) / (b/g2 · d/g1) — and, its second half, skips the product-scale gcd entirely (_from_coprime_ints): for two REDUCED operands the cross-reduced product is already in lowest terms (Knuth's theorem — a⊥b, c⊥d, a/g1 ⊥ d/g1, c/g2 ⊥ b/g2 together exclude every prime). This rc matches it, carrier-Python-only (srmech/amsc/q.py; the srmech_bigint / rational.rational_mul layers are untouched — this is the composite refinement above rc167/rc168's bignum mul+gcd work).
-
Q.__mul__/__rmul__→Q._mul, three tiers: (1) u64-fit operands delegate torational_mulunchanged — the fused mul+reduce scalar C op owns that tier, and the gate is fourbit_lengthprobes (_CROSS_REDUCE_MIN_BITS = 64, attested-to-structure: the u64 native scalar domain boundary); (2) big operands fromQ/int/bool/float(all reduced by invariant or construction) →_cross_reduce(the two cross gcds ride the Class-Icyclic.gcdon Class-K sign-branch magnitudes — never an ALUabs(); ≥1024-bit gcds keep dispatching tosrmech_bigint_gcd, the #765 self-hosting) then the product pair is built DIRECTLY via the new private trusted constructorQ._from_coprime— no product-scale gcd runs at all; (3) raw house-form(num, den)tuple/list operands (possibly UNREDUCED, possibly negative-den) take the general validating path_mul_cross_reduced(cross-reduce + fullrational_mulreduce — the canonicalValueErrorcontract for non-positive denominators is unchanged). -
BYTE-IDENTICAL, proven differentially:
tests/test_q_cross_gcd_mul_rc211.py(20 tests) pins new-Q.mul== old-pathrational_mul(a, b)(the exact pre-rc211 body) ==fractions.Fractionacross random operands at 8→2200 bits (below/at/above both the 64-bit gate and the 1024-bit bigq threshold), signs, zeros, ±1, u64 boundary slivers (2⁶³−1 … 2⁶⁴+1), cross-cancelling reciprocalsa/b × b/a(→ exactly(1, 1)), chaineda/b × b/c, shared prime-power pairs, One-scale (~100-digit) shared-factor operands, and the full interop surface (int/bool/float/tuple, reflected__rmul__,NotImplemented→TypeError,ValueErroron negative tuple dens at both size tiers). -
Measured (attested-to-measurement, Class B: Windows Python 3.14, pure tier
HAS_NATIVE=False, best-of-rounds, fair old-path = the pre-rc211_combine(other, rational_mul)body): small u64-fit parity (0.90–1.12×, the delegation gate is noise-level); coprime random ops ~3.0–3.9× faster at 128 → 20000 bits (the old path paid TWO product-scale gcds —rational_mul's reduce plusQ.__init__'s re-reduce — the fast path pays two operand-scale gcds and none at product scale); cross-cancelling reciprocals 1.2× → 12.9× faster (growing with size; 20000-bit: 49.6µs vs 637µs); One-scale shared-factor 1.36× faster. Generous-band perf guards committed in the test file; the intermediates now stay at operand scale (fora/b × b/athe multiply collapses to1·1/1·1). -
Honest note (known follow-up): the fast path's final raw product rides CPython
intmultiply —_nativeexposes no rawbigint_mul_c(only the fusedbigq_mul_c, whose built-in product-scale gcd is exactly the work the theorem eliminates). The heavy Euclid work (the two cross gcds) still self-hosts onsrmech_bigintabove the 1024-bit threshold; rc167 measured CPython-int vs srmech-bigint multiply at ≈cost-parity, so this is an architecture note (the #765 self-hosting discipline), not a perf gap. Exposing a rawsrmech_bigint_mulbinding and routing the coprime product through it above threshold is the tracked follow-up.
No C change (ABI stays 4), no new public callable (_from_coprime/_cross_reduce/_mul_cross_reduced are private) → tools.total stays 409, no registry regen, no rosetta ledger row moves, CEIL_NON_COMPUTE_OWED stays 0, CEIL_BIGNUM_REFERENCE stays 0. Consumer suites re-verified green (Q native-dispatch rc167 / Karatsuba rc168 / rational parity / pi_cascade / numeric-protocol conformance / Qalg / QMat / Qi / Qprime / Poly / gosper / zeilberger / wz / eigvec-exact / QMat-CRT).
[0.9.0rc211]¶
Restore libm-free — replace the LAST libm import in libsrmech (llrint in the best_rational_signed cascade) with a Class-K/N libm-free round (task #749; ships as 0.9.0rc211). srmech_cascade_best_rational_signed_f64 (c/src/srmech_cascade.c) rounded magnitude * fine_scale via llrint() — the ONLY llrint/lrint anywhere in c/src and the only libm symbol the built library imported. A .so/.dll resolves it at load, but a bare-C-host EXECUTABLE linking libsrmech then needs -lm, wrinkling the "libsrmech carries no libm" claim (held since the v0.7.0 C-transpile arc).
-
The replacement:
_cascade_brs_round_half_even(static,srmech_cascade.c) — round-half-to-even (banker's rounding) with no libm call, MATCHINGllrint()'s mode under the default IEEE-754FE_TONEARESTfenv (= Python's built-inround(), the documented parity contract): exact truncationt = (long long)v, exact fractional partfrac = v − (double)t(both exact under IEEE-754 for0 ≤ v < 2^63), thenfrac > 0.5 → t+1,frac == 0.5 → t + (t & 1)(the tie to the EVEN neighbour — the Class-K half-boundary branch), elset. Byte-identical tollrint()across the full range the cascade feeds it (0 ≤ v < 2^63;v ≥ 2^52is integral so it passes through exactly, andt+1cannot overflow since any round-up impliesv < 2^52). Noabs()/sign-tricks — the Class-K pin-slot upstream already stripped the sign, so the helper's domain is non-negative by construction.#include <math.h>is GONE fromsrmech_cascade.c. -
The
≥ 2^63guard (the one deliberate semantic edge). Products at/above2^63cannot be represented in the int64 ABI; previously they hitllrint()'s unspecified, platform-divergent domain-error result (x86-64:LLONG_MIN→ silent(0, 1); aarch64: saturateLLONG_MAX→ a nonsense convergent). Now the C peer returns an explicitSRMECH_ERR_BAD_INPUTand the Python dispatch falls through to the Python reference path — exact up to uint64 numerators (e.g.best_rational_signed(1e13)=(10**13, 1), product1e19 ∈ [2^63, 2^64)), honestValueErrorbeyond, the SAME behaviour the pure-Python surface always had. Native-enabled and pure-Python surfaces now agree where they previously diverged. -
Differential proof (committed,
tests/test_cascade_best_rational_signed_parity.py):test_rc211_round_mirror_matches_python_round_across_fed_rangepins the algorithm (statement-for-statement Python mirror) against Pythonround()(=llrint@FE_TONEAREST) across exact ties of both parities, the representability boundaries (2^52 − 0.5,2^52,2^53,2^63 − 1024), one-ulp-off-tie neighbours, subnormals, and a 2000-point seeded random sweep;test_rc211_native_round_differential_end_to_enddrives the SAME sweep through the shipped C binary (fine_scale=1,max_denominator=1) on both sign branches; plus the[2^63, 2^64)exact-fallback and≥ 2^64honest-ValueError pins.
C-only behavior change + docs (the compose.py docstrings promising "the C peer uses llrint()" updated to match; no Python logic touched). NO new op, NO ledger move — tools.total stays 409, CEIL_NON_COMPUTE_OWED stays 0, CEIL_BIGNUM_REFERENCE stays 0, no registry regen, no new ctypes symbol. ABI stays 4 (no wire-format change; one static helper added). JPL-clean (helper: 2 asserts, no loop; the cascade fn stays ≤ 60 lines). Whole-lib strict -std=c11 -Werror -Wpedantic -Wextra in both -O2 (asserts-live) and -DNDEBUG; ASAN/UBSAN clean; nm -D --undefined-only on the built .so shows no libm import (the llrint undefined-symbol row is gone).
[0.9.0rc210]¶
STOP-THE-LINE SOUNDNESS FIX — ThetaSum.is_zero rebuilt as a sound-True-only CERTIFICATE decision (Python + the C peer, ships as 0.9.0rc210). The shipped "structural interpolation completion" (rc98–rc103) CERTIFIED PROVABLY-NONZERO OBJECTS AS ZERO — false theorems from the exact elliptic-hypergeometric zero-decision — through four defects, all reproduced live on rc209:
- D1 (the band): the single-variable base case
_struct_one_vardecided ≡0 through a p-order bandk = max-term(Σe²)−1+3, which under-counts MULTI-TERM cancellation gaps. Witness A: a 6-term one-character ±-pair familyΣ c_u·θ(u·x^±)whose exact kernel cancels the lattice through p⁵ (first nonzero coefficient(p⁶, x⁰) = 1630980/2401> band 4) — shippedis_zero: True (a false theorem). (The #693 determination below fixed the DEGREE inside the band formula; rc210 removes the band itself — no per-term degree is a bound on a multi-term cancellation gap.) - D2 (the mixed-character node count):
_structural_is_zerointerpolated a MIXED-character sum atd+1 = max-term Σe² + 1nodes, a bound with NO supporting theorem (a sum of terms of different quasi-periodicity lies in no single theta-section space). Witness B:Σ_t c_t·θ(t·x³), t = 2..12, with the divided-difference kernelc_t = t⁴/∏_{s≠t}(t−s)— 11 pairwise-distinct characters, first nonzero coefficient(p¹⁵, x⁻¹⁵) = −1/12!far above the pretended band 11 — shippedis_zero: True (a false theorem). - D3 (dropped prefactor symbols):
_struct_variablesscanned theta arguments only, soa·θ(2x) − b·θ(2x)(a ≠ b) collapsed to(1−1)·θ(2x)and was certified zero. - D4 (node dedup): augment primes were not deduplicated against zero-node constants (a
θ(x/5)zero node IS the constant 5), so a duplicate node could under-count an interpolation.
The rebuild (Python srmech/amsc/thetasum.py): the True side was REPLACED, not repaired — the three-valued certificate recursion _decide_struct (ZERO / NONZERO / UNKNOWN; is_zero ⟺ ZERO; DEFAULT decline) proves zero ONLY via Z1 exact combine-cancellation + θ(1)=0, Z2 the Weierstrass three-term ±-pair reduction to the EMPTY normal form (Rosengren Eq. 1.12), generalized over a component's ACTUAL live symbols (the shipped fast path hardcoded x/y), Z3s the exact per-symbol joint-CHARACTER split (D_v = Σe² + the full Eq. 1.6 multiplier μ_v, ℚ-coefficient included — different characters are linearly independent over ℚ(q,p), so all components proven zero ⇒ zero), and *Z4** per-character elliptic interpolation at D_v+1 nodes pairwise distinct mod p^ℤ (Rosengren Cor. 1.3.5). There is NO numeric p-order band anywhere on the True side. The NONZERO certificates (N1 singleton component / N2 exact finite lattice detection / N3 nonzero node substitution / N4 nonzero character component) are DETECTION-ONLY — no detection depth ever produces a True. D3/D4 fixed (_struct_variables scans prefactors too; _pick_nodes dedups augment primes against zero-node constants). False = "not proven": each pre-rc210 false zero either becomes a proven NONZERO or an honest decline.
The C peer (srmech_thetasum_interp.c) rebuilt as the 1:1 mirror in the same rc — on native builds the false zero came FROM the C peer (the Python dispatch trusted it), so the fix is incomplete without it. Since the wire returns a bool and the ZERO side of the three-valued recursion is a pure AND-recursion (the N/U refinement never feeds a ZERO), the C mirror is exactly the certificate recursion's bool: an explicit-stack DFS whose branches are joint-character COMPONENTS or interpolation NODES; the old ti_one_var / ti_ps_* series-band machinery is DELETED outright. The Z2 stage rides the SAME single-copy pair-reduction kernels as the public ±-pair peer via the new internal header srmech_thetasum_internal.h (srmech_ts_recover_pairs / srmech_ts_reduce_syms — the reduce loop generalized from hardcoded {x,y} to an ordered symbol list; everything-mirrors forbids two copies of one reduction). The rc103 parallel peer's peel/replay is retargeted onto the certificate tree through the same deterministic ti_expand / ti_child_raw, preserving the byte-identical-verdict + order-free (chirality) contracts. Both ws sizers rebuilt (no series grid; the arena = the DFS path + the transient character table + the transient Z2 buffers). Same symbols, same wire ⇒ ABI stays 4; tools.total stays 409 (no new public op — the three-valued surface is internal). JPL-clean (caller-arena, no malloc, ≤60-line functions, ≥2 asserts, Class-K sign branches — never abs()).
Regression guards (committed): tests/test_thetasum_is_zero_sound_rc210.py — witnesses A/B/D3 pinned is_zero is False with INDEPENDENT exact proofs (pinned lowest lattice coefficients (p⁶,x⁰)=1630980/2401 and (p¹⁵,x⁻¹⁵)=−1/479001600, eval_trunc stabilisation cross-checks, the 11-distinct-characters structure), the committed rank-1 / rank-2 gap-kernel GENERATORS (computational provenance; any generated gap object must never be certified zero), the zero-side battery (three_term × common θ / shift_x / monomial-prefactor / disjoint sums stay PROVEN, fast path on AND off), and the documented honest declines (all-constant theta identities → False). tests/test_thetasum_iszero_corpus_parity_rc210.py + tests/data/thetasum_iszero_corpus_rc210.ndjson — the deduplicated 126-object corpus of every distinct cleared numerator the shipped elliptic suites decided at the rc209 baseline (74 True / 52 False): every shipped-True stays certificate-proven (fast path off too), every shipped-False is proven NONZERO, and on native builds the C verdict equals the pure verdict on EVERY object (0 mismatches, 0 declines — the rc99 parity pattern extended to the whole shipped corpus). Verified: the witnesses are False on BOTH the pure and native paths; the full 19-module elliptic/thetasum suite passes identically to baseline (137 passed / 19 skipped); the pre-rc210 "COMPLETE decision / trusted True AND False" docstring claims are rewritten to the sound-True-only certificate contract (module docstring, is_zero, _is_zero_py, _is_zero_interpolation, srmech.h).
[0.9.0rc209]¶
FIX the pre-existing macOS async-bus CI flake (#792) — bounded connect-retry in srmech.bus.aio.connect (ships as 0.9.0rc209). tests/test_bus_aio.py::test_concurrent_async_clients_round_trip intermittently failed on macos-14 CI (ConnectionRefusedError: [Errno 61] Connection refused, surfacing from _transport.py's s.connect(); hit rc173, rc208 — never a code regression). Root cause is a genuine client-side robustness gap with two racing faces: (a) the endpoint's socket file / registry appears at bind() a beat before listen() runs, so a fast client can connect into the pre-listen window; (b) a burst of simultaneous connects (the test fires N=10 via asyncio.gather, on top of not-yet-drained _wait_for_endpoint liveness-probe connections) momentarily overflows the server's listen(backlog=8) queue — and BSD kernels (macOS) refuse an AF_UNIX connect on a full backlog IMMEDIATELY with ECONNREFUSED, where Linux blocks until a slot frees. That kernel divergence is why only macOS flaked.
-
The load-bearing fix:
srmech/bus/aio.py_to_thread_connect_with_retry—aio.connectnow retries onlyConnectionRefusedErrorwith exponential backoff (first step 2 ms, per-step cap 100 ms, total bounded window 1.5 s), sleeping viaasyncio.sleepon the caller's loop (the loop is never blocked). On window exhaustion the lastConnectionRefusedErrorpropagates unchanged.FileNotFoundError("server not running") is NOT retried — it stays a fast, honest error. Each attempt constructs a fresh sync channel and a failed attempt closes its own socket in the transport layer, so retrying leaks nothing. This is a real robustness fix for any async client racing a just-started server, not a test accommodation. -
Belt-and-suspenders:
aio.servedefaultaccept_backlog8 → 64. Async callers routinely fire concurrent-connect bursts off one event loop, and macOS refuses at the full-backlog instant, so the async server default now comfortably exceeds realistic bursts. The syncservedefault stays 8 — matched to the C peer'sSRMECH_PLAT_STREAM_BACKLOG(parity preserved; the async bus layer is Python-asyncio-only with no C peer, so no C change and no C-parity obligation). -
Retry-path-exercised proof (new tests in
test_bus_aio.py, not dead code):test_aio_connect_retries_after_transient_refusal(synthetic: 3 refused attempts then success; asserts the attempt counter saw 4 calls),test_aio_connect_retries_real_econnrefused_until_listen(real kernel: UDS bound-but-not-listening refuses withECONNREFUSED,listen()fires 150 ms later, the retry absorbs the window and connects — the exact macOS-flake frame end-to-end),test_aio_connect_refusal_window_is_bounded(never-listening endpoint →ConnectionRefusedErrorpropagates after ≈ the full 1.5 s window, no infinite retry),test_aio_connect_missing_endpoint_fails_fast(FileNotFoundErrordoes not ride the retry window).
No new public callable (the retry is an existing function's body + a private helper) → tools.total stays 409, no registry regen, no rosetta ledger row moves, CEIL_NON_COMPUTE_OWED stays 0, CEIL_BIGNUM_REFERENCE stays 0. ABI stays 4 (no C change at all). Robustness stress-verified: the concurrency tests (test_concurrent_async_clients_round_trip + test_concurrent_sends_on_one_channel) looped ≥100 iterations green on Linux, plus a high-N (N=50) burst variant against the fixed layer.
[0.9.0rc208]¶
RESPONSION — the response-function COMPUTE op family (F1186: the op⊗operand⊗responsion k=3 completion; ships as 0.9.0rc208). The op⊗operand DUALITY (A-N operator verbs ⊗ carrier operand nouns = field⊗excitation) completes at k=3 with the responsion — the answering-correspondence between successive op-on-operand applications, the stored relationship itself (srmech = Stored-RELATIONSHIP Mechanism — the responsion is literally the package's reason for being). The exact/discrete regime sees one op(operand) = result; the continuous/asymptotic regime (the beat, the resolvent, the propagator) is where the responsion lives. This rc lands it as the third verb-family: the general response function of a generator L acting on an excitation u0, generalizing EPH's e^{−zL}.
-
srmech.amsc.laplacian.responsion(L, u0, z, *, kind="propagator") -> Vec. TWO canonical continuous-form members that are Laplace-transform DUALS — a tight, framework-honest family, not a grab-bag.kind="propagator"(time domain):e^{−zL}·u0— this IS the shipped EPHpropagate(rc136) and the call DELEGATES to it verbatim (same arg(z) coherence dial, same mandatory 2π seam-fold, same native/pure dispatch).responsionSUBSUMESpropagateas its time-domain member;propagateSTAYS as the named EPH surface (back-compat + thepropagate_wound/propagate_sparse/eph_harvestsibling family hangs off it — the framework-honest split: responsion is the family verb, propagate is one face of it).kind="resolvent"(frequency/energy domain — the Green's function):(zI − L)^{−1}·u0— NEW: the Laplace transform of the semigroup propagator,(zI − L)^{−1} = ∫₀^∞ e^{−zt}·e^{tL} dtforRe z > max λ(L)(withe^{tL}·u0 = propagate(L, u0, −t)), per eigenmode the dual paire^{−z·λ} ⟷ 1/(z − λ). Realised as a REAL complex linear solve(zI − L)·x = u0via the real 2n×2n block embedding[[Aᵣ,−Aᵢ],[Aᵢ,Aᵣ]]·[u;v] = [bᵣ;bᵢ]— the SAME embeddingmat_solve's complex path already rides.zexactly in the spectrum ofLis a resolvent POLE and raisesZeroDivisionErrorhonestly on BOTH paths (the pole IS the physics — never a garbage number). Noresponsion_rankwas added:eph_harvestalready IS the Born-rank read over the propagator member; duplicating it generically would be surface inflation, not completion. -
SAME-RC 1:1 C peer
srmech_responsion(+srmech_responsion_arena_bytes) (everything-mirrors).c/src/srmech_responsion.c: kind 0 is a pure pass-through delegation to the shippedsrmech_eph_propagate(identical arena carve); kind 1 builds the 2n×2n block embedding + stacked RHS in the caller arena and composes the shippedsrmech_dense_solve_f64_wsGauss–Jordan kernel — a composition of existing C, no forked solve — so a bare-C host runs BOTH members. Caller-arena (no malloc), JPL-clean (≤60-line fns, ≥2 asserts, no goto/abs; the only magnitude logic is the composed kernel's existing Class-K sign-branch pivot). Pure Python is the complete alternative (_dense_solve_complex— the same embedding overmat_solve). -
LEDGER —
responsion→c_dispatchedFROM BIRTH.CEIL_NON_COMPUTE_OWEDstays 0,CEIL_BIGNUM_REFERENCEstays 0.tools.total408 → 409 (one new ToolEntry; all pinned count-asserts updated); the C tool-registry table AND the carrier-registry back-index regenerated to match.
ABI stays 4 (additive: two new symbols — no existing wire format changes). ⚠ The new ctypes bindings (srmech_responsion, srmech_responsion_arena_bytes) carry explicit .argtypes/.restype (size_t→c_size_t — the rc201 marshal lesson). Attested by tests/test_responsion_rc208.py: kind="propagator" exactly equal to propagate (native tier AND forced-pure tier), the resolvent residual ‖(zI−L)·x − u0‖ ≤ 1e-9 (real L / real z, real L / complex z, complex-Hermitian L / complex z), the Laplace-dual eigen-check (independent eigendecomposition: per-mode c_res,k·(z−λ_k) == c_0,k vs c_prop,k == e^{−zλ_k}·c_0,k), the quadrature Laplace-transform identity (∫₀^∞ e^{−zt}·e^{tL}u0 dt ≈ resolvent by Simpson over propagate(L, u0, −t)), the honest resolvent-pole ZeroDivisionError (native AND pure), native == pure parity, read-only inputs, contracts, and registration. Whole-lib strict -std=c11 -Werror -Wpedantic -Wextra in both -O2 (asserts-live) and -DNDEBUG; ASAN/UBSAN clean.
[0.9.0rc207]¶
The WOUND EPH propagator — the 2π seam-fold's divmod quotient KEPT (closes gh #1276; the #741 mod-should-be-divmod audit's first concrete instance; ships as 0.9.0rc207). rc136's propagate(L, u0, z) = e^{−zL}·u0 argument-reduces each per-mode oscillation argument Im(z)·λ_k modulo 2π (the mandatory seam-fold). That fold IS a divmod: quotient w_k = round(Im(z)·λ_k / 2π) = the metacycle winding (the whole 2π turns — thrown away, the mod-collapse), remainder θ_k = the epicycle residue (|θ| ≤ π — what propagate returns). propagate's own docstring named the debt; this rc pays it: keep the GRADING — both harvests at the seam, wired in the One's the_one(σ, θ, w) crank vocabulary.
-
srmech.amsc.laplacian.propagate_wound(L, u0, z) -> dict. The SAME harvest aspropagate— byte-identical at the same dispatch tier (same cascade, same order; carryingwdoes not perturb it) — PLUS the per-mode readout, JSON-native arrays in eigensolve mode order:harvest_re/harvest_im,eigenvalues,winding(whole-ℤ metacycle turns, never% 2),theta(the folded epicycle residue;2π·w_k + θ_kreconstructsIm(z)·λ_kLOSSLESSLY on the fold grid — theOne.unwrapped_phasereconstruction per mode),sigma_effective(±1, the tower-graded chirality dial via the winding's divmod binary tower on the mode triad(w_k, 0, 0)— NOT the melding barew mod 2;w=5andw=7are DISTINGUISHED),spinor_sign(±1, the double-cover(−1)^{w_k}). Lift a mode into a full One withthe_one(+1, θ_num, θ_den, w=(w_k, 0, 0)). -
ONE divmod, no forked constant, readouts REUSED (never re-derived). Pure: the fold inside
_eph_cos_sinis extracted as_eph_seam_fold(θ) -> (w, qn)— the SAME exact Machin-2π (_EPH_TWO_PI) divmodpropagatefolds with, quotient kept;_eph_cos_sincalls it and discardsw(byte-identical trig),propagate_woundcalls it and keepsw. The chirality readouts delegate to the One's EXISTING gh#1276 winding surface —One.sigma_effective/One.spinor_signbodies extracted to module-level_sigma_effective_from_triad/_spinor_sign_from_triad(the methods delegate; byte-identical; the nativesrmech_sigma_effective/srmech_spinor_signpeers keep dispatching). -
SAME-RC 1:1 C peers (everything-mirrors). New
srmech_winding_fold(theta, *w_out, *theta_out)(c/src/srmech_trig.c) — the divmod on the SAME integer Q642/πquarter-turn machinerysrmech_cos/srmech_sinalready fold with (the internaltrig_reduceoctant fold refactored totrig_reduce_kwith the FULL quotient kept;trig_reducewraps it byte-identically),w = round(θ/2π)round-half-toward-+∞ (the Python_eph_round_divconvention),|θ_fold| ≤ πread offk = 4w + oct_rel+ the Q61 remainder — the (w, θ) pair IS the trig fold's own divmod, just kept. Newsrmech_eph_propagate_wound(+_arena_bytes) (c/src/srmech_eph_propagate.c) — thesrmech_eph_propagatecascade (shared statics; the Hermitian lift extracted toeph_lift_hermitian, used by both, so the two harvests are byte-identical by construction) + the per-mode readout composingsrmech_winding_fold+ the EXISTINGsrmech_sigma_effective/srmech_spinor_sign. Caller-arena (no malloc), JPL-clean (≤60-line fns, ≥2 asserts, no goto/abs). -
LEDGER —
propagate_wound→c_dispatchedFROM BIRTH.CEIL_NON_COMPUTE_OWEDstays 0,CEIL_BIGNUM_REFERENCEstays 0.tools.total407 → 408 (one new ToolEntry; all pinned count-asserts updated); the C tool-registry table AND the carrier-registry back-index (Mat/Vecgainpropagate_wound) regenerated to match.
ABI stays 4 (additive: three new symbols — no existing wire format changes). ⚠ The new ctypes bindings (srmech_eph_propagate_wound, srmech_eph_propagate_wound_arena_bytes, srmech_winding_fold) carry explicit .argtypes/.restype (size_t→c_size_t, int64_t*→POINTER(c_int64), int32_t*→POINTER(c_int32) — the rc201 marshal lesson). Attested by tests/test_eph_propagate_wound_rc207.py: the epicycle harvest byte-identical to propagate (native tier AND forced-pure tier), the lossless 2π·w + θ round-trip per mode vs the raw Im(z)·λ_k, hand-checked windings on a diagonal L, the tower-graded σ_eff anti-collapse (w=5 vs w=7), native == pure parity (winding/σ/spinor exact-integer equal; θ/harvest within-tol), the thermal z (w ≡ 0) limit, read-only inputs, contracts, and registration. Whole-lib strict -std=c11 -Werror -Wpedantic -Wextra in both -O2 (asserts-live) and -DNDEBUG.
[0.9.0rc206]¶
The SPARSE-SCALED EPH propagator — the corpus-scale residual of gh #1274 (item 1c; ships as 0.9.0rc206). rc136 shipped EPH — propagate(L, u0, z) = e^{−zL}·u0, the ONE complex-time Wick-rotation propagator with the arg(z) coherence dial + the mandatory 2π seam-fold — but computed it via the EIGENBASIS (symmetric_eigendecompose), which is O(n³) and capped at n ≤ 256 native. gh #1274's item 1c — the sparse-scaled propagator that runs on a corpus-scale L with matrix-vector products only — was the residual. This rc ships it.
-
srmech.amsc.laplacian.propagate_sparse(n, edges, weights=None, *, u0, z, tol=1e-10, max_degree=2048) -> Vec. The SAME harvest aspropagate(same complex-z convention, same coherence dial: z real → thermal, z imaginary → coherent, between → partial; same seam-folded Wick factor; same complex-Vecreturn contract) computed by a Chebyshev polynomial of the operator applied with matvecs only — NO eigendecomposition, NO densee^{−zL}—O(m·n_edges)time /O(n)memory, so it runs past then ≤ 256dense-eigensolve cap. The operator is the SIGNED graph Laplacian read straight off the edge list (thesigned_laplaciansemantics on thefiedler_sparse/spectral_spinesparse-input convention:deg = Σ|w|by Class-K sign branch — neverabs(); self-loops skipped; duplicate edges read per-edge). Compose the Born-rule read over it exactly aseph_harvestcomposes overpropagate. -
The method (deterministic Chebyshev — chosen over Lanczos: no orthogonalisation state, no breakdown modes, bit-reproducible C/Python twinning; Lanczos remains the fallback if a future regime outgrows it). Spectral interval
[0, 2·max_i deg_i]by Gershgorin (cheap, deterministic, PSD-safe; an overestimate only widens the interval), affine-mapped to[−1, 1]. Chebyshev interpolation coefficients ofe^{−z·λ(s)}from the Chebyshev nodes — each node value via the rc136 Wick-factor machinery (Class-N exp + the MANDATORY Machin-2π seam-folded cos/sin in Python;srmech_exp+srmech_cos/srmech_sinwith the Q61 octant reduction in C),cos(k·θ_j)by the 3-term recurrence (no per-(k,j) trig). The node count adaptively DOUBLES 64 → the HARD CAPmax_degree+1(JPL Rule 2), accepting when the coefficient tail (the top eighth — the aliasing guard) falls belowtol·max|e^{−z·λ}|(compared in Class-K magnitude-SQUARES — noabs(), no sqrt). Then the forwardT_{k+1} = 2·L̃·T_k − T_{k−1}vector recurrence (‖T_k‖ ≤ 1on[−1,1]→ stable). Honest non-convergence: the tail not below tol withinmax_degree→ValueError(pure) /SRMECH_ERR_OVERFLOW(C) — raisemax_degreeor shrink|z|; the tolerance is never silently degraded. Convergence regime stated honestly in the docstring: needed degree ~|z|·λ_max/2 + O(log 1/tol)(thermal truncates earliest; coherent needs the full wave zone; backwardRe z < 0converges with error relative to the max propagator magnitude over the interval). -
SAME-RC 1:1 C peer
srmech_eph_propagate_sparse(everything-mirrors).c/src/srmech_eph_propagate_sparse.c: degrees + Gershgorin + node evaluation + the discrete Chebyshev transform + the matvec recurrence all standalone-C over the existing Class-N kernels (srmech_exp/srmech_cos/srmech_sin/srmech_atan— π = 4·atan(1), no libm), caller-arena (srmech_eph_propagate_sparse_arena_bytes(n, n_edges, max_degree); no malloc), JPL-clean (≤60-line fns, ≥2 asserts, no goto/abs; the finiteness guard is a single-line macro, no Rule-5 exemption added). NUMERIC (FPU-tol): native == pure agree WITHIN-TOL (same algorithm, same accumulation order — differential-tested), not byte-for-byte. -
LEDGER —
propagate_sparse→c_dispatchedFROM BIRTH. A numeric-float COMPUTE op:CEIL_NON_COMPUTE_OWEDstays 0,CEIL_BIGNUM_REFERENCEstays 0.tools.total406 → 407 (one new ToolEntry; all pinned count-asserts updated); the C tool-registry table (c/src/srmech_tool_registry.c) regenerated to match.
ABI stays 4 (additive: two new symbols — no existing wire format changes). ⚠ The new ctypes bindings (srmech_eph_propagate_sparse, srmech_eph_propagate_sparse_arena_bytes) carry explicit .argtypes/.restype (size_t→c_size_t, uint32_t*→POINTER(c_uint32) — the rc201 marshal lesson). Attested by tests/test_eph_propagate_sparse_rc206.py: the differential vs the rc136 eigenbasis propagate on n ≤ 256 (thermal + coherent + partial z, unit + signed weights), an independent Taylor-matvec reference, a corpus-scale n > 256 case (past the eigenbasis cap) checked for P(0) = u0 / semigroup P(z₁)P(z₂) ≈ P(z₁+z₂) / coherent norm conservation, native == pure parity, the honest non-convergence contract, read-only inputs, and registration. Whole-lib strict -std=c11 -Werror -Wpedantic -Wextra in both -O2 (asserts-live) and -DNDEBUG.
[0.9.0rc205]¶
carrier_schema() — the CARRIER (operand) introspection surface, the noun-side DUAL of tool_schema (gh #1293; the Siona / RBS-LM self-hosting finding — R-RBS-LM-FINDING_1110, UPSTREAM_NOTES §91; ships as 0.9.0rc205). srmech.amsc.tool_schema exposes the ops (the A–N operator verbs) richly, but the carrier TYPES (the operand nouns) were not first-class introspectable: a consumer had to scrape carrier_ladder_descriptor() (ladder/rung ints + adds_variable) with no human-readable description per carrier, so introspection could not say what a TriPoly IS beyond "rung 3" — Poly/BiPoly/TriPoly differed only by a number. This rc ships the rich noun-side surface, so tool_schema + carrier_schema together let ANY consumer (Siona's introspect_carriers, which can now thin to a pass-through; a human reader) discover BOTH the verbs and the nouns.
-
srmech.amsc.carrier_schema.carrier_schema() -> dict. Per carrier — the 24 operand types on the public op surface: the ordinary/q variable ladders (Poly/BiPoly/TriPoly,QPoly/QBiPoly), the Cayley–Dickson rungs (float/complex/quaternion/octonion/sedenionat dims ½/4/8/16 = the descriptor's R/C/H/O/S), the float-LA carriers (Mat/Vec/HV), the exact scalars (int/Fraction/Q), the elliptic row (EllMonomial/EllRatio/ThetaSum), the weight-axis pair (UnaryTheta/MockQSeries/HarmonicMaass), and the HDC objects (One/SedenionRegister) — returns{"name", "description"(one line: what it is, its variable semantics, when to use it), "ladder", "rung", "variables", "ops": {"consumes", "produces"}}keyed by carrier name.ladder/rungagree withcarrier_ladder_descriptor()(tested). -
The
opsback-index is DERIVED, never hand-maintained: a word-boundary token scan of every registered ToolEntry's declaredparameters[].type/returns.typestrings, unioned with the rc120 per-op CARRIER CONTRACT (carrier_ladder._OP_CONTRACTS) resolved through the ladder rung → carrier-name map (so the Cayley–Dickson ops — whose ToolEntry types saylist[float]— still index underquaternion/octonion/sedenion). A new op that consumes/produces a carrier joins the back-index automatically; the codegen idempotence + hash ratchets force the C table regen. -
SAME-RC C peer (everything-mirrors;
CEIL_NON_COMPUTE_OWEDHOLDS at 0). The authored metadata + derived back-index are code-generated into the compiled-insrmech_carrier_registryconst table (c/tools/gen_carrier_registry.py→c/src/srmech_carrier_registry.c— the rc184gen_tool_registry/ rc202gen_class_registrycodegen model; each per-carrier payload baked as its pre-canonical compact-ASCII JSON fragment, rows in byte-sorted name order). Accessors + assembler inc/src/srmech_carrier_schema.c:srmech_carrier_registry_count/_get/_find(name/description/ladder/rung as first-class struct fields) +srmech_carrier_schema(buf, buf_len, out_len)(two-pass NULL-buffer size-query contract) whose output is BYTE-IDENTICAL tojson.dumps(_pure_carrier_schema(), sort_keys=True, separators=(",", ":"))— the sha256 hash-ratchet locking the C table to the Python SSoT (tests/test_carrier_schema_rc205.py).carrier_schema()native-dispatches to it whenHAS_NATIVE(and no profile tools are registered); the pure derivation is the complete fallback. JPL-clean (no malloc — caller buffer; ≤60-line fns; ≥2 asserts; no goto/abs/libm). -
LEDGER —
carrier_schema→non_compute/composes_cFROM BIRTH (it dispatches to its same-rc C peer; pure fallback complete):CEIL_NON_COMPUTE_OWEDstays 0,CEIL_BIGNUM_REFERENCEstays 0; composes_c 117 → 118, non_compute sum 176 → 177 (the three pinned-split ratchets updated).tools.total405 → 406 (one new ToolEntry, all pinned count-asserts updated); the C tool-registry table (c/src/srmech_tool_registry.c) regenerated to match.
ABI stays 4 (additive: four new symbols + one struct — no existing wire format changes). ⚠ The new ctypes bindings (srmech_carrier_schema, srmech_carrier_registry_{count,get,find}) carry explicit .argtypes/.restype (size_t→c_size_t, struct-pointer returns via the _SrmechCarrierEntryC mirror — the rc201 marshal lesson). A drift ratchet in the new test pins that every srmech carrier-class token appearing in ToolEntry type strings is either in the registry or on an explicit non-carrier infra allowlist, so a future carrier cannot ship un-introspectable.
[0.9.0rc204]¶
The spectral SPINE — the DOMINANT-mode read-out that completes the community/spine pair (gh#1324; F1167–F1169; ships as 0.9.0rc204). srmech already reads the LOW modes of a graph Laplacian — fiedler_vector / fiedler_sparse (the λ₂ navigation / 2-way normalized cut) and three_fold_eigvec_groups (the 3-way low/mid/high band split). The DUAL read was missing: the DOMINANT eigenvector (largest λ) of a (signed) Laplacian concentrates on the structurally CENTRAL items, so its top-|component| nodes ARE the spine. This rc ships that read-out, domain-free (edges = any relational graph: siona describe-spine; ephemerides central bodies).
-
srmech.amsc.laplacian.spectral_spine(edges, weights=None, *, k=8) -> list[int]. Build the signed LaplacianL = D̄ − Afrom the edge list (n inferred as one past the largest endpoint — isolated high-index nodes are never central), take the DOMINANT eigenvector viasymmetric_eigendecompose, and return the top-min(k, n)nodes by |component|. Ranking is the Class-K magnitude-squarere²+im²(NOabs(), NO sqrt), descending, ties by ascending index. -
srmech.amsc.laplacian.relational_structure(edges, weights=None) -> dict. The ergonomic sugar that composes the atoms in ONE eigendecomposition:{"spine": [...], "communities": [left, right], "coherence": λ₂}— the DOMINANT-mode spine, the Fiedler 2-way sign bisection (thenormalized_cut_bisectconvention), and the algebraic connectivity λ₂. -
1:1 C peer
srmech_spectral_spine(same-rc, everything-mirrors). A Class-L COMPOSITE over the existing kernelssrmech_graph_dense_adjacency+srmech_hermitian_eigendecompose_ws(c/src/srmech_spectral_spine.c): build the signed Laplacian in the caller arena, ONE eigensolve, top-k select — no new eigensolver, no malloc (JPL-clean: caller-arena, ≤60-line fns, ≥2 asserts, no goto/abs/libm).spectral_spinedispatches to it whenHAS_NATIVE; the pure-Python cascade (signed_laplacian+symmetric_eigendecompose+ top-k) is the complete alternative for a no-C host. NUMERIC (the eigenvector basis is non-unique) → native == pure WITHIN-TOL, the selected index set / order stable for a non-degenerate dominant eigenvalue, NOT byte-for-byte. -
LEDGER —
spectral_spine→c_dispatched,relational_structure→composition_of_c. Both are COMPUTE ops (numeric float, Class-L); neither touchesnon_compute/bignum_reference(CEIL_NON_COMPUTE_OWEDstays 0,CEIL_BIGNUM_REFERENCEstays 0).tools.total403 → 405 (two new ToolEntries); the C tool-registry table (c/src/srmech_tool_registry.c) regenerated to match.
ABI stays 4 (additive: two new symbols — no existing wire format changes, no callback typedef). ⚠ The new ctypes bindings (srmech_spectral_spine, srmech_spectral_spine_arena_bytes) carry explicit .argtypes/.restype (the size_t→c_size_t / uint32_t*→POINTER(c_uint32) marshal that cost rc201 a CI cycle). Attested by tests/test_spectral_spine_rc204.py (hand-checked star-graph spine + native==pure parity + relational_structure + edge cases + registration). Whole-lib strict -std=c11 -Werror -Wpedantic -Wextra in both -O2 (asserts-live) and -DNDEBUG.
[0.9.0rc203]¶
run_class_method → C — the FINAL owed orchestration row; CEIL_NON_COMPUTE_OWED 1 → 0, the everything-to-C program COMPLETE (make_class → C arc, #887; ships as 0.9.0rc203). rc201/rc201b landed the object-model ENGINE (srmech_make_class_run); rc202 (this ship) builds the STATELESS one-shot srmech_run_class_method on top — the C peer of srmech.dsl._class_surface.run_class_method. A bare-C host now RESOLVES a class NAME to its packaged [class] descriptor, runs one method, and wraps the result entirely in C, so nothing of the DSL class surface is left owed to Python. With this row discharged, no owed_orchestration row remains — a bare-C host runs the WHOLE apparatus.
-
The NAME → DESCRIPTOR resolve, compiled-in (no Python, no filesystem). The four shipped
[class]descriptors (one.toml/hurwitz.toml/genome.toml/sedenion_register.toml) are emitted as aconstC data table —c/src/srmech_class_registry.c, GENERATED by the newc/tools/gen_class_registry.py(the rc184gen_tool_registry.pycodegen model: each descriptor astatic const unsigned char[], LF-normalised, referenced by a{name, toml, toml_len}srmech_class_descriptor_trow; idempotence-guarded, JPL-clean const data).srmech_class_descriptor_lookup(name, &len)resolves a shipped NAME to its descriptor bytes IN C. (Rejected alternatives: the rc172 catalog-C registry indexes ATTESTED ROOTS — a different object; a host-FS read would push the resolve LOGIC out of C.) -
srmech_run_class_method(the new C symbol; JSON-in/JSON-out). Resolveclass_name→ descriptor → the rc201srmech_make_class_runengine (via the sharedmc_run_from_tomlspine) → WRAP as{"class", "method", "result", "fields"}(insertion-order, byte-identical to the purerun_class_methoddict).srmech_run_class_method_arena_bytes(class_name, fields_len, args_len)sizes the caller arena (resolving the descriptor length internally). Every engine-covered method across the three shipped classes dispatches byte-identically — One's inline accessors, SedenionRegister'snavmap/slots/is_navigable/navigate/write/materialize/read/carry/correct, Genome'sadd_chromosome/recall/assemble/partition. -
The DEFER contract (rc103 inform-don't-limit). An unknown /
register_class_dirUSER class (not in the compiled-in registry) or an engine-deferred leaf (the One bignumflat/matrix/scalar, the float couple/uncouple, the host-FS disk quartet) sets*out_kind = SRMECH_MAKE_CLASS_DEFERand the caller runs the COMPLETE purerun_class_method— never a wrong answer. -
LEDGER —
srmech.dsl.run_class_methodmovedowed_orchestration→composes_c;CEIL_NON_COMPUTE_OWED1 → 0. The last owed row is discharged: the resolve + construct + invoke + wrap LOGIC runs standalone in C. The everything-to-C ("no boundary") program is COMPLETE.
ABI stays 4 (additive: two new symbols + the const registry table + the srmech_class_descriptor_t struct — no existing wire format changes, no callback typedef). ⚠ The new ctypes bindings (srmech_run_class_method, srmech_run_class_method_arena_bytes, srmech_class_descriptor_lookup) carry explicit .argtypes/.restype (the size_t→c_size_t marshal that cost rc201 a CI cycle). Attested by tests/test_run_class_method_c_rc202.py (the C surface vs the pure run_class_method across every engine-covered class/method + the resolve-in-C + DEFER contract) with no regression to tests/test_make_class_engine_c_rc201{,b}.py. JPL-clean C (caller-arena, ≤60-line fns, ≥2 asserts, no goto/malloc/abs/libm); whole-lib strict -std=c11 -Werror -Wpedantic -Wextra in both -O2 (asserts-live) and -DNDEBUG.
[0.9.0rc202]¶
make_class → C, the HEAVY-carrier vtable + the state-route machinery (engine 2/2 — make_class DISCHARGED to composes_c, #887). rc201 landed the object-model ENGINE core + the plain / returns="self" spine PROVEN; rc202 (ships as 0.9.0rc202) wires the HEAVY-carrier leaves and the remaining state-route machinery into srmech_make_class_run (c/src/srmech_make_class.c), so the C engine now RUNS the DSL [class] object model across all route types — byte-identical to srmech.dsl._class_catalog.CatalogClass.
-
The state-route machinery (all routes now in C). rc201's plain +
returns="self"spine gainsmutates(the op returns(ret, {field: new}); the named fields are validated ⊆ the declared list + replaced,retemitted),appends(list.appendone field),sets(replace one field), and the multi-opchain(a{op, binds, as}stage pipeline threading each stage'sasresult into a small scope, then state-routed like a single op). The emitted{"result": …, "fields": …}carries the POST-route field state (grown / replaced), byte-identical to the pure object model. -
SedenionRegister HDC-storage leaves (composing
srmech_mint_vector+srmech_hdc_bind/bundle/hamming+srmech_hamming_*).write(mutatesslots+codebook: mintVAL:keyif absent, record the slot assignment),materialize(bundle_k bind(ADDR[k], value_k)over the sorted slots, a__pad__mint padding an even count to odd —chiral_flip= the exact Class-C byte-reverse for asign<0value),read(the 2-stage chain: unbind the slot address from the bundle → the noisy vector → nearest-codebook clean →(key, sign); the argmax uses the EXACT integer chirality magnitude|D − 2·hamming|, the peer of|1 − 2h/D|, so NO float enters the decision),carry(Hamming(2ⁿ−1) encode),correct(Hamming decode + single-bit correct →{data, error_position, corrected_codeword}). -
Genome byte/HV leaves (composing
srmech_genome_chromosome/recall/genome/partition).add_chromosome(appendschromosomes: a CHROM cap overlabel+ each Klein-4 leaf coupled throughthe_one→ the list of leaf_dim chunks),recall(skip caps + re-bindthe_one→ the recovered leaves),assemble(a{label: [leaves]}dict → ONE multi-kernel strand),partition(the inverse →{label: [leaves]}, replicating the strand-order-uniq /labels=-order / overwrite-on-duplicate dict semantics exactly). -
The remaining DEFERs (rc103 inform-don't-limit — the mval carrier genuinely cannot emit these byte-identically). The One bignum leaves
flat/scalar(exact rationals overflow the int64 mval carrier — a 249-bit trace numerator forθ=½) +matrix(float within-tol, not byte-identical);Hurwitz.generate(returns a liveOneobject, not JSON); the sedcouple_working/uncouple_working(float working word); the genome disk quartetsave/load/catalog/append(host-FS, not a compute leaf) +shape/cap. Each sets*out_kind = SRMECH_MAKE_CLASS_DEFERand the caller runs the COMPLETE pureCatalogClass. -
LEDGER —
srmech.dsl.make_classmovedowed_orchestration→composes_c;CEIL_NON_COMPUTE_OWED2 → 1. The C engine composes the shipped C leaves to run the object model across every route type; the honest split keeps the mval-unrepresentable leaves DEFERRING to pure. The 1 owed row left =run_class_method(the stateless JSON wrapper over this engine — rc202-next).
ABI stays 4 (additive: rc201b wires EXISTING leaf symbols — srmech_mint_vector / srmech_hdc_* / srmech_genome_* / srmech_hamming_* — into the engine; no new symbol, no callback typedef, no wire change). Attested by the extended tests/test_make_class_engine_c_rc201.py (the shrunk DEFER list) + the new tests/test_make_class_engine_c_rc201b.py (the C engine vs the pure CatalogClass for every wired heavy leaf + route: sed write/materialize/read/carry/correct + genome add_chromosome/recall/assemble/partition, incl. the partition dedup/reorder/filter dict cases). JPL-clean C (caller-arena, ≤60-line fns, ≥2 asserts, no goto/malloc/abs/libm); whole-lib strict -std=c11 -Werror -Wpedantic -Wextra in both -O2 (asserts-live) and -DNDEBUG.
[0.9.0rc201]¶
make_class → C, the OBJECT-MODEL ENGINE (engine core + a proven vtable batch; the make_class → C arc capstone begins, #887). rc194-200 made all 31 make_class LEAF ops C-realizable; rc201 builds the ORCHESTRATION on top — the descriptor → field-state → dispatch → route ENGINE that a bare-C host uses to RUN the DSL [class] object model, mirroring srmech.dsl._class_catalog.CatalogClass one layer down. Ships in c/src/srmech_make_class.c, declared in srmech.h, ctypes-bound + wrapped (make_class_run_c / has_native_make_class) in the walk-excluded _native.
-
srmech_make_class_run(the new C engine symbol; JSON-in/JSON-out). Takes the packaged[class]descriptor TOML + amethodname + the instance FIELD-STATE and call ARGS as JSON objects, and for a method in the rc201 batch runs it IN C, writing{"result": …, "fields": …}as canonical JSON (byte-identical tojson.dumps(serialise_native(…))). It (1) PARSES the descriptor viasrmech_toml_parse/srmech_toml_table_get→ the[class].field/[class].methodtables; (2) builds the FIELD-STATE DICT carrier (srmech_mval_t) from the[class.field]defaults (list*→[],dict*→{}, else supplied-or-NONE) overlaid with the supplied fields — mirroringCatalogClass.__init__; (3) DISPATCHES a single-opmethod, marshalling itsbindspositionally from (args → fields); (4) resolves the op through a LEAF VTABLE returning LIVE carriers (distinct from the rc188 invoke_tool text vtable — these compose); (5) applies the state ROUTE. Thesrmech_make_class_run_arena_bytes(toml_len, fields_len, args_len)helper sizes the caller arena (the TOML tree + two JSON trees + two mval trees + the result). -
The rc201 PROVEN BATCH (native == pure
CatalogClass, byte-identical / value-identical). One (plain op): the 5 INLINE-CONSTANT accessorsdim/imag_dims/partition/plane_counts/grammar_slots(emit14/(1,3,7)/(1,3,7,3)/(0,1,3)/('B','H','N')with no leaf call). SedenionRegister (plain op):navmap→srmech_sedenion_navmap,slots→srmech_sed_slots,is_navigable→srmech_sedenion_is_navigable. SedenionRegister (returns="self" route):navigate→srmech_sedenion_navigate— routes every slot name by ×e_j into a NEW register's{D, codebook, slots}field-state DICT (self untouched, the codebook rides through unchanged), the first C realisation of the multi-field new-instance route. -
The DEFER contract (rc103 inform-don't-limit). Any method OUTSIDE the batch — a
chain, anappends/sets/mutatesroute, an op whose leaf is the heavy-carrier batch (the One bignum leavesflat/matrix/scalar+ Hurwitz.generate; the genome byte/HV + disk leaves; the sed HDC-storage leaveswrite/materialize/read), an unknown method/class, or an unparseable descriptor — sets*out_kind = SRMECH_MAKE_CLASS_DEFERand the caller runs the COMPLETE pureCatalogClass(never a wrong answer). A userregister_class_dirclass DEFERS the same way (no host op-resolver callback → ABI stays 4). -
HONEST SPLIT — make_class stays OWED this rc; rc201b discharges (CEIL_NON_COMPUTE_OWED stays 2). The 31 leaves span bignum (One), byte/HV + disk (genome), HDC + Cayley-Dickson (sedenion) carriers with an all-four-class byte-identical proof — genuinely >1 clean rc. rc201 lands the ENGINE + the plain + returns="self" spine PROVEN; the ledger row
srmech.dsl.make_classis only honestlycomposes_conce the engine runs the object model across ALL its route types (appends/mutates/chain, exercised only by the genome HV + sed HDC leaves), so it staysowed_orchestrationuntil rc201b wires the heavy-carrier vtable + those routes. rc202 =run_class_method(the stateless JSON wrapper over this engine).
ABI stays 4 (additive symbols, no new callback typedef). No new public op (the _native prover binding is walk-excluded). Attested by tests/test_make_class_engine_c_rc201.py (the C engine vs the pure CatalogClass: One's 5 accessors × θ-variants + the sedenion navmap/slots/is_navigable/navigate returns="self", plus the DEFER contract for the heavy leaves + unknown method/class). JPL-clean C (caller-arena, ≤60-line fns, ≥2 asserts, no goto/malloc/abs/libm); whole-lib strict -std=c11 -Werror -Wpedantic -Wextra in both -O2 (asserts-live) and -DNDEBUG.
[0.9.0rc200]¶
make_class → C, leaf-batch 6 — the sedenion HDC-STORAGE leaves + the srmech_mint_vector foundation (COMPLETES all 31 make_class leaf ops in C). The sixth (and heaviest) leaf-batch of the make_class → C arc (#887): the sedenion_register.toml [class] descriptor's remaining 4 HDC-STORAGE leaves (sed_write / sed_materialize / sed_read_unbind / sed_clean) now run standalone-C, so a bare-C host (and the rc201 object-model engine) writes / materialises / reads / cleans "Siona's address layer" natively. With rc199's 8 address-algebra leaves, all 12 sedenion_register leaves — and every make_class leaf across the genome + sedenion classes — are C-realizable.
srmech_mint_vector(the ONE new C symbol; the foundation). The deterministic RBS-HDC vector minter (Class-A content addressing) in C, byte-for-byte mirroring Pythonsrmech.signal_processing.mint_vector: it fillsout[0:n_bytes]with the SHA-256 chainSHA256(name ‖ u64_be(0)) ‖ SHA256(name ‖ u64_be(1)) ‖ …truncated ton_bytes, where each counter is an 8-byte BIG-ENDIAN unsigned integer concatenated after the raw UTF-8namebytes, chained (0,1,2,…) until filled. Routes through the srmech SHA-256 core — the name's full 64-byte blocks compress ONCE into a Merkle-Damgård midstate, then each counter re-finalises the shared tail — so it is bounded-stack (no malloc), JPL-clean, and byte-identical to the pure chain. Ships inc/src/srmech_sha256.c, declared insrmech.h, ctypes-bound + wrapped (mint_vector_c) in the walk-excluded_native.- Python
mint_vectordispatches tosrmech_mint_vectorwhenHAS_NATIVE(byte-identical; the pure SHA-256 chain is the complete fallback for a no-C host / Pyodide) → it movescomposition_of_c → c_dispatched. This is the transparent hinge: the 4 HDC-storage leaves already composemint_vector(address + value minting, the"__pad__"odd-N tie-break) plus the already-c_dispatchedsrmech_hdc_bind/srmech_hdc_bundle/srmech_hdc_similarity(viahdc.bind/bundle/similarity), so making the minter native turns all four into genuine compositions of C — they staycomposition_of_c.sed_clean's nearest-codebook cleanup keeps its explicit Class-K magnitude tie-break (>=for the +sense,>for the −sense; neverabs()) and skips the"__pad__"sentinel exactly as the Python branch. The HDC store is FUZZY, so the read/clean parity contract is deterministic-same-decision (not byte-exact like the address leaves).
ABI stays 4 (additive symbol, no new callback typedef). No new public op. Attested by tests/test_sed_storage_c_rc200.py (native == pure mint_vector byte-identical; sed_read(sed_write(...)) round-trip recovers the key; materialize / read_unbind / clean native == pure decision).
[0.9.0rc199]¶
make_class → C, leaf-batch 5 — the sedenion ADDRESS-ALGEBRA leaves (sed_navmap / sed_navigate / sed_is_navigable / sed_carry / sed_correct / sed_slots / sed_couple_working / sed_uncouple_working). The fifth leaf-batch of the make_class → C arc (#887): the sedenion_register.toml [class] descriptor binds 12 leaf ops, and rc199 finishes the 8 ADDRESS-ALGEBRA leaves so a bare-C host (and the rc201 object-model engine) navigates / carries / corrects "Siona's address layer" natively — no per-method Python shell-out. Mostly marshalling glue over ALREADY-SHIPPED C kernels + ONE new trivial reshape; the rc200 leaf-batch does the 4 HDC-storage leaves (sed_write / sed_materialize / sed_read_unbind / sed_clean).
Each address leaf now dispatches to a SINGLE dedicated C peer when HAS_NATIVE, with the exact pure Python retained as the fallback + parity oracle (inform-don't-limit: any out-of-C-domain shape routes to pure, which raises the exact ValueError):
sed_navmap→ the reusedsrmech_sedenion_navmap(the whole 16-slot signed permutation in ONE call, vs the purecd_basis_productcocycle loop). BYTE-IDENTICAL{i: (dest, sign)}.sed_navigate→ the reusedsrmech_sedenion_navigate(the slot routing;D+codebookpass through unchanged, only the slot names route + the Class-C signs compose). BYTE-IDENTICAL routed{slot: (key, sign)}.sed_correct→ the reusedsrmech_hamming_decode_correct(the whole locate + correct + extract in ONE C call — previouslysed_correctreached onlysrmech_hamming_syndromevia the pure composition). BYTE-IDENTICAL{data, error_position, corrected_codeword}(data+ position from C; thecorrected_codewordreconstructed from the located position exactly as pure).sed_slots→ the ONE newsrmech_sed_slots— the trivial dict-normalise reshape (validate + copy the register's occupied(slot, sign)skeleton; slot ∈ [0,16), sign ∈ {±1}). The slot int-keys ride thesrmech_mval_tDICT as STR"0".."15"one layer up (the JSON-object round-trip); the Python callerint()s them before the C sees plain int arrays, and the key STRINGS pass through in Python (a bare-C host holds them alongside). BYTE-IDENTICAL for every real register state.sed_is_navigable→ the reusedsrmech_sedenion_is_navigable(the bignum-free modular-rank reversibility gate) — ALREADY dispatched vialeft_mult_is_invertible. Byte-identical bool.sed_carry→ the reusedsrmech_hamming_encode(the GF(2) XOR encode) — ALREADY dispatched viahamming_encode. Byte-identical codeword.sed_couple_working/sed_uncouple_working→ the reusedsrmech_hypercomplex_couple_q61(the exact-Q61 octonion coupler) — ALREADY dispatched viahypercomplex_couple/_couple_q61. WITHIN-TOL (≤1e-9) on the final float projection of the reversible ≤7-stream working word (the ONLY float boundary; the Q61 core is exact).
Ships the one new symbol in c/src/srmech_sedenion.c (caller-arena, no malloc/goto, no abs(), ≤60-line functions, ≥2 asserts, runtime NULL-check-before-assert), declared in srmech.h and ctypes-bound + wrapped (sedenion_navmap_c / sedenion_navigate_c / hamming_decode_correct_c / sed_slots_c) in the walk-excluded _native.
LEDGER — no bucket changes. The 8 address leaves were ALREADY standalone-ready — composition_of_c (the 7: each dispatches to its dedicated C peer for the plain path AND retains a pure composition-of-c_dispatched fallback branch + the leaf's dict/list return-shape marshalling) or non_compute/composes_c (sed_slots, a pure accessor that now composes srmech_sed_slots). Building the dedicated C peers strengthens standalone-readiness WITHOUT re-bucketing — each is still a composition with pure variant branches, exactly as rc197/rc198 kept chromosome / recall / genome / partition as composition_of_c. CEIL_NON_COMPUTE_OWED stays 2; debt/bignum/c_exists_unbound/numpy_carrier stay 0. No new public callable (the sed_* leaves are the make_class binding surface, exempt in test_tool_schema_coverage.py, and the new C peer + wrappers live in _native), so tools stay 403 (no new ToolEntry) and no ratchet re-pin.
No ABI change — one additive C symbol, no new callback typedef, so SRMECH_ABI_VERSION stays 4. Both C build modes clean (-std=c11 -Wall -Wextra -Wpedantic -Werror asserts-live -O2 AND -DNDEBUG, CMAKE_C_EXTENSIONS OFF strict-c11); numpy-free; JPL green. New test tests/test_sedenion_addr_c_rc199.py (all 8 address leaves pure-vs-native: byte-identical for the 6 EXACT, within-tol for the 2 NUM; the srmech_sed_slots domain-defer + navigate-permutation + decode-correct facts). Next leaf-batch: the 4 HDC-storage sed_* leaves (rc200).
[0.9.0rc198]¶
make_class → C, leaf-batch 4 — the genome MULTI-KERNEL + PARTITION in-memory leaves (srmech_genome_genome + srmech_genome_partition), COMPLETING the genome leaf-family in C. The fourth (and final in-memory) leaf-batch of the make_class → C arc (#887): the genome.toml [class] descriptor binds 10 leaf ops, and rc198 ships the C peers of the two multi-kernel leaves so a bare-C host (and the rc201 object-model engine) assembles / splits a whole genome strand natively — no per-method Python shell-out. Both LOOP the rc197 in-memory leaves (srmech_genome_chromosome to assemble, the recall re-bind to recover) and reuse the rc196 cap foundation (genome_pack_cap / genome_cap_kind / genome_decode_label) verbatim. The genome in-memory ops are BYTE-EXACT (cap framing + the reversible Klein-4 XOR, not fuzzy HDC), so both peers are byte-identical portable. Ships in c/src/srmech_genome.c (caller-arena, no malloc/goto, no abs(), ≤60-line functions, ≥2 asserts, runtime NULL-check-before-assert):
-
srmech_genome_genome— the plain multi-kernel assemble. Mirrorsgenome(kernels, the_one)for the plain (single-gene-per-chromosome) path: each labelled kernel becomes a CHROM-capped chromosome (LOOPING the rc197srmech_genome_chromosomeverbatim — the cap ⊕ per-turnsrmech_klein4_bind), concatenated in kernel order into ONE strand(n_kernels + Σ leaf_counts) × leaf_dimbytes. The kernels are marshalled as concatenated labels (labels+label_lens[]) + concatenated leaves (leaves+leaf_counts[]) +n_kernels.srmech.amsc.genome.genomedispatches to it whenHAS_NATIVEfor the plain path; the recovered strand reconstructs via_hv_from_block, BYTE-IDENTICAL to the pure per-kernelchromosomeloop (the numpy-free fallback + parity oracle). The §44chromosomes=multi-gene assembly form opens its own inline gene caps and stays in the pure Python (inform-don't-limit); an over-long label / non-uniform leaf routes to pure (which raises the exactValueError). -
srmech_genome_partition— the multi-kernel split (the inverse). Mirrorspartition(strand, the_one, labels=None): walk the strand's fixed-widthleaf_dim-byte blocks, OPEN a partition on each CHROM / kernel-telomere / active-telomere cap (label read INLINE bygenome_decode_label), SKIP every gene / regulatory / boolean / threshold / graded / kernel-header cap (the partition FLATTENS across genes — gate-agnostic like recall), and re-bind each data turn throughthe_oneas that partition's leaf. Returns the ORDERED partitions (labels + per-partition leaf counts + the recovered leaves); the Python caller builds the{label: [leaves]}dict (preserving the pure walk's dict overwrite-on-duplicate-label semantics —out[label] = …per partition in order, last wins) and applies thelabels=filter.srmech.amsc.genome.partitiondispatches to it whenHAS_NATIVEand the strand is uniform fixed-width blocks; a non-uniform strand routes to the pure walk. Verifiedpartition(genome({...}, one), one) == {...}byte-identical pure-vs-native over many kernel counts × labels ×the_onevalues × leaf_dims, including multi-gene strands (flatten), duplicate labels, empty kernels, and thelabels=filter.
The genome leaf-family is now COMPLETE in C — all 10 genome.toml leaf ops are C-realizable: encode_shape + telomere (rc196), chromosome + recall (rc197), genome + partition (rc198), and the 4 FS ops genome_save / genome_load / genome_catalog / genome_append already have C peers (srmech_genome_{save,load,catalog,append}) + native-dispatching Python wrappers (legitimate host-FS dispatch, not a shell). Ready for the rc201 make_class leaf-vtable.
LEDGER — no bucket changes. genome.genome + genome.partition were ALREADY composition_of_c (standalone-ready compositions of the c_dispatched chromosome / klein4_bind); the dedicated C peers strengthen that (a single symbol now backs each) without re-bucketing — each is still a composition (per-kernel chromosome build / per-cap partition walk) with pure variant branches, so composition_of_c stays the honest bucket. CEIL_NON_COMPUTE_OWED stays 2; debt/bignum/c_exists_unbound/numpy_carrier stay 0. No new public callable (the C peers + genome_genome_c / genome_partition_c wrappers live in the walk-excluded _native), so tools stay 403 (no new ToolEntry) and no ratchet re-pin.
No ABI change — additive C symbols only, no new callback typedef, so SRMECH_ABI_VERSION stays 4. Both C build modes clean (-std=c11 -Wall -Wextra -Wpedantic -Werror asserts-live -O2 AND -DNDEBUG, CMAKE_C_EXTENSIONS OFF strict-c11); numpy-free; JPL green. New test tests/test_genome_multikernel_c_rc198.py (genome + partition byte-identical native-vs-pure; the FS-peer readiness assertions). The in-memory genome leaf-family is complete; next leaf-batch: the sed_* domain leaves that block make_class / run_class_method (the rc201 object-model engine).
[0.9.0rc197]¶
make_class → C, leaf-batch 3 — the genome CHROMOSOME + RECALL in-memory leaves (srmech_genome_chromosome + srmech_genome_recall). The third leaf-batch of the make_class → C arc (#887): the genome.toml [class] descriptor binds chromosome (method add_chromosome) + recall, and rc197 ships their C peers so a bare-C host (and the rc201 object-model engine) builds / reverses a chromosome strand natively — no per-method Python shell-out. The genome in-memory ops are BYTE-EXACT (cap framing + the reversible Klein-4 XOR, not fuzzy HDC), so both peers are byte-identical portable. Ships in c/src/srmech_genome.c (caller-arena, no malloc/goto, no abs(), ≤60-line functions, ≥2 asserts, runtime NULL-check-before-assert):
-
srmech_genome_chromosome— the plain single-kernel strand builder. Mirrorschromosome(leaves, the_one, label=…)for the plain path: a leading CHROM telomere cap overlabel(REUSING the rc196genome_pack_capcap writer verbatim), then each ofn_leavesleaves (eachleaf_dimbytes, contiguous) coupled throughthe_oneviasrmech_klein4_bind— the reversible Klein-4 XOR that isquad_turn. The output strand is(1 + n_leaves) × leaf_dimbytes.srmech.amsc.genome.chromosomedispatches to it whenHAS_NATIVEfor the plain path; the recovered strand reconstructs via the existing_hv_from_block(the CHROM cap →HV(sectors=256), each turn →HV(sectors=4)), BYTE-IDENTICAL to the pure list-comp (the numpy-free fallback + parity oracle). The gene / kernel / active-telomere chromosome forms open their own boundary caps (_gene_cap/_kernel_telomere/_pack_active_telomere) and stay in the pure Python — rc197 covers exactly the plain path the class method binds; those variant surfaces defer to pure for a later leaf-batch (rc103 inform-don't-limit). -
srmech_genome_recall— the plain-path leaf recovery. Mirrorsrecall(strand, the_one): walk the strand's fixed-widthleaf_dim-byte blocks, SKIP every cap (REUSING the rc196genome_cap_kindfirst-byte classifier — the nine §44 markers), and re-bind each data turn throughthe_oneviasrmech_klein4_bind(the bind is its own inverse) to recover the original leaf.srmech.amsc.genome.recalldispatches to it whenHAS_NATIVEand the strand is uniform fixed-widthleaf_dimblocks. recall is gate-agnostic (it flattens across CHROM / GENE / kernel / active-telomere caps alike), so the one C peer correctly recovers a multi-gene strand too; a non-uniform strand (e.g. a variable-width packed turn) routes to the pure walk. Verifiedrecall(chromosome(leaves, one, label=L), one) == leavesbyte-identical pure-vs-native over many leaf counts × labels ×the_onevalues × leaf_dims, including the variant (gene / kernel / active_count) chromosome forms defer-to-pure correctly.
LEDGER — no bucket changes. genome.chromosome + genome.recall were ALREADY composition_of_c (standalone-ready compositions of the c_dispatched telomere / klein4_bind); the dedicated C peers strengthen that (a single symbol now backs each in-memory op) without re-bucketing — the op is still a composition (cap ⊕ per-turn bind) with pure variant branches, so composition_of_c stays the honest bucket. CEIL_NON_COMPUTE_OWED stays 2; debt/bignum/c_exists_unbound/numpy_carrier stay 0. No new public callable (the C peers + genome_chromosome_c / genome_recall_c wrappers live in the walk-excluded _native), so tools stay 403 (no new ToolEntry) and no ratchet re-pin.
No ABI change — additive C symbols only, no new callback typedef, so SRMECH_ABI_VERSION stays 4. Both C build modes clean (-std=c11 -Wall -Wextra -Wpedantic -Werror asserts-live -O2 AND -DNDEBUG, CMAKE_C_EXTENSIONS OFF strict-c11); numpy-free; JPL green. Next leaf-batch: the genome / partition multi-kernel leaves (rc198).
[0.9.0rc196]¶
make_class → C, leaf-batch 2 — the genome CAP FOUNDATION (srmech_genome_encode_shape + srmech_genome_telomere). The second leaf-batch of the make_class → C arc (#887): the genome.toml [class] descriptor binds 10 leaf ops, and rc196 ships the C peers of the two smallest in-memory leaves so a bare-C host (and the rc201 object-model engine) runs them natively — plus the shared cap byte-TLV helpers that rc197 (chromosome/recall) and rc198 (genome/partition) build on. The genome in-memory ops are BYTE-EXACT (cap framing + reversible Klein-4 XOR, not fuzzy HDC), so everything here is byte-identical portable. Ships in c/src/srmech_genome.c (caller-arena, no malloc/goto, no abs(), ≤60-line functions, ≥2 asserts, runtime NULL-check-before-assert):
-
srmech_genome_encode_shape— the pure-INTEGER shape planner.n → (leaves, depth)whereleaves = ceil(n / 256)(overflow-safe split divide) anddepth = ceil(log4(leaves))(Class-I/N integer only, no float).srmech.amsc.genome.encode_shapenow dispatches to it whenHAS_NATIVE(the ceil-div + ceil-log4 arithmetic runs in C); the caller mapsdepth → shape("tome"/"mobius"/"quad_strand") + assembles the{n, shape, leaves, depth, leaf_cap}dict, so the result is BYTE-IDENTICAL to the pure Python (which is the numpy-free fallback + parity oracle, and handlesn ≥ 2**64that the uint64 C path routes back). -
srmech_genome_telomere— the CHROM boundary cap WRITER. Packs the fixed-widthdim-byte §44 cap leaf[0x43] + label, NUL-padded to dim(byte-identical to the bytes the puretelomerewraps inHV(sectors=256)). This is the FIRST C cap-WRITER (the genome C surface until now only READ/scanned caps).srmech.amsc.genome.telomeredispatches to it whenHAS_NATIVE; an over-long label routes to the pure_pack_capso the exactValueErrorstill surfaces. -
The shared cap helpers in C (the load-bearing foundation).
genome_pack_cap(the generic[marker] + label, NUL-padwriter, mirror of_pack_cap— used bysrmech_genome_telomere, reused by rc197/rc198 for every chromosome/gene/kernel cap) +genome_cap_kind(the first-byte kind classifier, mirror of_cap_kind— the nine §44 markers or −1; de-duplicates the marker list ingenome_block_len). The READ inverse of_pack_capis the existinggenome_decode_label(label, bytes[1:]up to first NUL) +block[0](marker) — together the C_unpack_cap. The exact on-disk cap format rc197/rc198 reuse verbatim: adim-byte block, byte 0 = marker (> 3), bytes[1 : 1+len(label)]= raw UTF-8 label, remainder NUL padding.
LEDGER. Both ops move to c_dispatched (a real C symbol now dispatches them): genome.encode_shape (was non_compute/composes_c — a pure-integer in-memory compute op now dispatching to a dedicated C symbol, parallel to telomere_tick / gene_express) and genome.telomere (was composition_of_c). Only encode_shape leaves non_compute → composes_c 116 → 115, non_compute total 177 → 176; CEIL_NON_COMPUTE_OWED stays 2 (make_class / run_class_method, discharged in rc201/rc202); debt/bignum/c_exists_unbound/numpy_carrier stay 0.
No ABI change — additive C symbols only, no new callback typedef, so SRMECH_ABI_VERSION stays 4; tools stay 403 (no new ToolEntry — the two ops already had public Python surface; the C peers + private helpers live in the walk-excluded _native / are static). Both C build modes clean (-std=c11 -Wall -Wextra -Wpedantic -Werror asserts-live -O2 AND -DNDEBUG); numpy-free; JPL green. Pure-vs-native byte-identical for encode_shape (over a wide n sweep) + telomere (over labels × dims).
[0.9.0rc195]¶
make_class → C, leaf-batch 1 — the One-family COMPUTE leaf ops in C (srmech_one_scalar + srmech_one_matrix). The first leaf-batch of the make_class → C arc (#887): the one.toml / hurwitz.toml [class] descriptors bind accessor methods to the module-level flat ops in srmech.amsc.cascade.one, and two of those are genuine COMPUTE leaves that assemble the S(σ,θ) generator into a scalar / matrix. rc195 ships their C peers so the rc201 object-model engine (and any bare-C host) runs One.scalar() / One.matrix() natively — no per-method Python shell-out. Ships in c/src/srmech_one.c (both peers COMPOSE the rc138 srmech_the_one: regenerate the 14 exact adjoint rationals, then assemble exactly like the Python; caller-arena srmech_bigint, no malloc/goto, no abs(), ≤60-line functions, ≥2 asserts):
-
srmech_one_scalar(+srmech_one_scalar_ws_bound) — the EXACT scalar projection.mode 0trace (Tr G = 3 + 3σ + 8σ·cos θ),mode 1sqnorm (Σ (num/den)²over the 14 state rationals),mode 2component (theindex-th rational). Returns the reduced exact(num, den)oversrmech_bigint— BYTE-IDENTICAL to the pure PythonOne.to_scalar(verified over both chiralities × 8 θ × 3 terms).srmech.amsc.cascade.to_scalarnow dispatches to it whenHAS_NATIVE(the whole trace/sqnorm/component arithmetic runs in C, not just the cos/sin); theas_float=Trueterminalnum/dencast stays in Python (a single boundary cast, no C float, so still byte-identical native-vs-pure). The pure body is the complete alternative + the numpy-free fallback + the parity oracle. -
srmech_one_matrix(+srmech_one_matrix_ws_bound) — the 14×14 float operator G(σ,θ). 196 row-major doubles =⨁_n (1 ⊕ σ R_n(θ)). cos/sin are read from the exact flat rationals + rounded to double (a math.h-free bignum-rational→double:|num| << 96, floor-divide byden, scale the small quotient back by2⁻⁹⁶), then the ±1 / ±cos / ±sin Fano tile is placed with NO float accumulation (FMA-safe). NUMERIC — the opt-in lossy[scientific]realisation, WITHIN-TOL (≤ 1e-12) to the pureOne.to_matrix, NOT byte-identical (empirically the max element diff is0.0on every tested θ). The PythonOne.to_matrix/one_matrixflat-op are LEFT as the purecn/cdrealisation (native-vs-pure stays byte-identical — the existing rc138 / class-catalog.tolist() == .tolist()guarantees are preserved);srmech_one_matrixis the bare-C-host mirror, proven within-tol equivalent intests/test_one_leaf_ops_c_rc195.py.
R1 — the One↔DICT contract for the rc201 object model. The One frozen dataclass has 6 fields — sigma, theta, terms, blocks, winding, spinor — but blocks is a pure derivation of (sigma, theta, terms) (via srmech_the_one) and spinor of (sigma, theta, terms, winding), so the value-carrying ADJOINT state (everything the 9 rc195 leaf ops read) round-trips through the DICT {"sigma": int, "theta": [num, den], "terms": int} — the_one(sigma, θn, θd, terms) reconstructs identical sigma/theta/terms/blocks, and srmech_the_one regenerates the same 14 flat rationals. The winding triad is the SEPARATE gh#1276 surface (its own srmech_winding_* C peers) and every rc195 leaf op is w-BLIND (the adjoint folds the winding away), so the DICT is faithful for this batch; a wound One would need winding added to the DICT, but no one.toml/hurwitz.toml method touches it.
LEDGER — no bucket changes (the One family was ALREADY correctly bucketed). the_one / one_matrix / to_scalar have been c_dispatched since rc138 (on srmech_the_one); rc195 makes the whole assembly native too (strengthening, not re-bucketing). one_flat_rational + the 5 structural-constant accessors (one_dim/one_imag_dims/one_partition/one_plane_counts/one_grammar_slots) stay non_compute/composes_c — they are pure accessors / structural constants that hide no compute (the ledger's own composes_c definition, which explicitly lists the_one-composing accessors). No live public-op surface change (the C helpers live in the walk-excluded _native), so non_compute 177 = owed 2 / composes_c 116 / host_glue 15 / dev_tooling 44 is UNCHANGED; CEIL_NON_COMPUTE_OWED stays 2; debt/bignum/c_exists_unbound/numpy_carrier stay 0.
No ABI change — additive C symbols only, no new callback typedef, so SRMECH_ABI_VERSION stays 4; tools stay 403 (no new ToolEntry — the leaf ops are class-TOML-only, exempt in the tool-schema coverage gate). Both C build modes clean (-std=c11 -Wall -Wextra -Wpedantic -Werror asserts-live -O2 AND -DNDEBUG; the CMake CMAKE_C_EXTENSIONS OFF strict-c11 that CI uses); numpy-free; JPL green (test_jpl_audit.py). New test tests/test_one_leaf_ops_c_rc195.py (scalar byte-identical native-vs-pure over all modes / chiralities / θ / terms + as_float; matrix within-tol + the independent cos/sin reference; the Python One.to_matrix still byte-identical native-vs-pure). Next leaf-batches: the genome / sed_* domain leaves that block make_class / run_class_method (the rc201 object-model engine).
[0.9.0rc194]¶
HOST-GLUE — the MCP HTTP+SSE transport in C (serve_http_sse owed → composes_c; CEIL_NON_COMPUTE_OWED 3 → 2). A bare-C host (no Python) now serves MCP over a cross-terminal HTTP+Server-Sent-Events transport on a localhost TCP port — the C peer of srmech.mcp._sse.serve_http_sse. It COMPOSES the rc186 srmech_mcp_handle (JSON-RPC dispatch, defer_calls == 0 — the same bare-C-host discipline as serve_stdio; tools/call returns the honest defer error, so the transport is complete while dispatch stays the rc188 invoke_tool concern) over a NEW rc194 TCP PAL surface. Ships in c/src/srmech_mcp_sse.c + c/src/srmech_platform.c:
-
The NEW TCP PAL surface (
srmech_platform.{h,c}).srmech_plat_has_tcp/srmech_plat_tcp_{listen,accept,server_close,read_some,write_all,conn_close}(localhost BSD sockets:SO_REUSEADDR, kernel-assigned port readback viagetsockname) +srmech_plat_sleep_ms. The accept is poll-gated (atimeout_ms+ a*gotflag) so the accept loop re-checks its stop flag each tick — the rc180 socket-teardown NO-HANG discipline. Distinct from the AF_UNIX / named-pipe STREAM IPC surface (that is addressed by a short name; TCP is addressed by host:port and carries an HTTP byte stream). -
srmech_mcp_sse_serve(host, port, out_handle)+srmech_mcp_sse_port+srmech_mcp_sse_stop(handle-based) +srmech_mcp_serve_http_sse(host, port)(blocking serve-forever). A background accept thread routes GET /sse (register a session, emit theendpointevent carrying/message?session=<id>, leave the connection open) + POST /message?session=(parse the body, dispatch via srmech_mcp_handle, push the JSON-RPC response over the matching SSE stream as amessageevent, return 202) + GET /healthz ({"status": "ok"}); a second thread emits a 15s: keepaliveon idle sessions. The HTTP/1.1 request parse (bounded — GET/POST, path,?session=, Content-Length), the SSE event framing (event:/data:/\n\n, byte-exact with_sse._sse_emit), and the bounded (≤8) mutex-guarded session registry are OS-agnostic. No per-request allocation (the accept-thread scratch is allocated once at serve —RULE_3_COLD_PATH_FILES, likesrmech_bus.c). -
NO-HANG teardown (the rc180 discipline).
srmech_mcp_sse_stopsets the stop flag (under the registry mutex — the cross-thread flag accesses are synchronized), closes the poll-gated listener, joins both threads (they return within one poll tick), closes all sessions, frees the handle. Verified prompt (< 3s) even with an open SSE session held by a reader. -
POSIX-FIRST (documented limitation). The rc194 TCP PAL is POSIX today; on Windows
srmech_plat_has_tcp()reports 0 (Winsock is a tracked follow-up) and the C server declines (SRMECH_ERR_BAD_INPUT) so a Python host runs the purehttp.server. The Pythonserve_http_ssedefault (background) stays pure — it keeps the fullinvoke_tool(tools/call executes), exactly likeserve_stdio; the native C server wires only thebackground=Falsebare-C-host serve-forever path (hasattr-guarded → old libs / a no-TCP host fall through to the pure server). The SSE server-driving test is@posix_only(skips on Windows → no CI hang).
LEDGER (owed_orchestration → composes_c). srmech.mcp._sse.serve_http_sse discharges: the C peer serves the whole transport, the pure path flows the per-request lifecycle through the C srmech_mcp_handle, and the transitive walk reaches no NOT-READY leaf. non_compute 177 = owed 2 / composes_c 116 / host_glue 15 / dev_tooling 44 (owed 3 → 2; composes_c 115 → 116; sum stays 177). CEIL_NON_COMPUTE_OWED 3 → 2. The 2 owed left = make_class / run_class_method (HONEST-DEFERRED — the domain-leaf arc, leaf-op-blocked on the genome / sed_* C backlog).
No ABI change — additive C symbols only, and the server dispatches in C (NO Python callback typedef), so ABI stays 4; tools stay 403 (no new ToolEntry — the transport is non_compute); debt/bignum/c_exists_unbound stay 0. Both C build modes clean (-Wall/-Wextra/-Wpedantic asserts-live for pytest + -DNDEBUG -Werror -Wpedantic -Wextra Release); ASan/UBSan clean over the HTTP parse + SSE framing + POST body + healthz / 404 / missing-session / malformed request + accept/teardown + NULL-args (no leaks); ThreadSanitizer clean over the concurrent session registry + push + keepalive + teardown; numpy-free; JPL green. New test tests/test_mcp_sse_c_rc194.py (the GET /sse endpoint event; the POST → 202 + response-over-SSE round-trip; native == pure byte-parity; healthz; 404; missing-session 400; prompt no-hang teardown — POSIX-first). Next: make_class / run_class_method (the domain-leaf arc, scoped separately).
[0.9.0rc193]¶
HOST-GLUE — the CLI arg-GRAMMAR + dispatch in C (the last big ledger-mover; owed 10 → 3). A bare-C host (no Python) now parses the srmech console-script grammar for all five subcommands + routes each to its (C) run body. The C peer of srmech.cli.main.{build_parser, main} + the five srmech.cli.{status,bus,dsl,mcp,klass}.add_arguments. Ships in c/src/srmech_cli.c:
-
srmech_cli_parse(argc, argv, out, out_cap, out_len, out_action, out_exit)— the bounded arg-parser. Reproduces the WHOLE grammar EXACTLY asbuild_parser+ the fiveadd_arguments: the topsrmechprog +--version, the five subcommands (statusflat;bus{list,tap,pipe,send,serve};dsl{run,ops,visualize};mcp{emit-mcpb};class{list,describe}), each subcommand's flags / positionals /choices/ defaults. On a VALID invocation it emits the parsed argparse namespace as canonical JSON (dest keys, defaults filled) and sets*out_action = SRMECH_CLI_ACTION_RUN; numeric options are validated (--pid/--limitlex as int64 → JSON number,--poll-interval/--timeoutlex as a float token → JSON string the consumer float()s). Bounded — a fixed subcommand table + a fixed per-subcommand option table (JPL Rule 2), no arena (argv parsed in place, JSON written straight toout). -
srmech_cli_dispatch(parsed_json, len, out_route)— the routing. Reads the top-level"command"and sets*out_routeto the run-body a bare-Cmain()invokes (SRMECH_CLI_ROUTE_{STATUS,BUS,DSL,MCP,CLASS}, or_HELPfor a baresrmech). The subcommand run bodies (cli.*.run) were ALREADYcomposes_cover the now-C bus/dsl/mcp — this rc builds the arg-GRAMMAR + dispatch that ROUTES to them. -
Behavior-parity, NOT byte-identical help text (the documented split).
srmech.cli.main.mainruns argv throughsrmech_cli_parse+srmech_cli_dispatchon the clean RUN path (hasattr-guarded → a stale/pure host stays pure), reconstructs the argparse Namespace + calls the same run body.-h/--help(ACTION_HELP),--version(ACTION_VERSION), an argparse arg error (ACTION_ERROR, exit 2), and anything the bounded parser will not risk (an option abbreviation, an inline--, an unusual numeric token, a value that itself looks like an option →SRMECH_ERR_NOT_IMPL) DEFER to pure argparse — which emits byte-identical help/version/error text + exit codes (inform-don't-limit; never a wrong answer). VERIFIED: for representative argv per subcommand (valid RUN + no-subcommand +--help+--version+ bad-choice + missing-positional + bad-int + unknown-flag +bogus) the nativemain()and forced-puremain()produce IDENTICAL exit code AND stdout/stderr; and the C-reconstructed Namespace ==build_parser().parse_args(argv)for every RUN case.
LEDGER (owed_orchestration → composes_c ×7). cli.main.{main, build_parser} + cli.{bus,dsl,mcp,klass,status}.add_arguments discharge: main runtime-dispatches through the C parse+dispatch; build_parser + each add_arguments are the pure-fallback grammar SSoT whose C peer is proven equivalent by the behavior-parity test (the rc185 get_tool_schema "pure constructor with a proven-equivalent C peer" precedent). non_compute 177 = owed 3 / composes_c 115 / host_glue 15 / dev_tooling 44 (owed 10 → 3; composes_c 108 → 115; sum stays 177). CEIL_NON_COMPUTE_OWED 10 → 3. The 3 owed left = make_class / run_class_method (HONEST-DEFERRED — leaf-op-blocked on the genome / sed_* domain-leaf C backlog) + serve_http_sse (OPTIONAL — Claude Code uses stdio; a separate C-HTTPS/SSE arc). The HOST-GLUE phase reaches its practical completion at CEIL 3.
No ABI change (additive C symbols only — srmech_cli_parse / srmech_cli_dispatch; ABI stays 4); tools stay 403 (no new ToolEntry — the CLI grammar is non_compute); debt/bignum/c_exists_unbound stay 0. Both C build modes clean (-Wall/-Wextra/-Wpedantic asserts-live for pytest + -DNDEBUG -Werror -Wpedantic -Wextra Release); ASan/UBSan clean over the parser (each subcommand's valid argv + a bad-arg + --help + --version + a NULL-arg graceful return + srmech_cli_dispatch over each route); numpy-free. New test tests/test_cli_c_rc193.py (native == pure Namespace per subcommand; the ACTION/ROUTE contract; end-to-end main() behavior-parity native-vs-pure; help/version/error defer; the NULL-arg graceful return). Next: the remaining #796 spectral infer row (the float-Mat eigensolve + dict path).
[0.9.0rc192]¶
#796 PAYOFF — the SIGMA-DEFINITE (wz_certificate) infer row now DISPATCHES its reducer DECISION in C (2/7 → 3/7 router-rows run in C for a bare-C host). rc176 built the F929 OPEN/infer router in C for two rows (cyclic → the_one, sigma-gosper → gosper); rc191 shipped the nested exact-ℚ srmech_carrier_read_bipoly reader. This rc wires that reader into srmech_infer.c so the DEFINITE-sum wz_certificate row runs in C — a composes_c widening of the existing srmech_infer (NO ledger move). Ships in c/src/srmech_infer.c:
-
The sigma-DEFINITE (wz) C path — the GENUINE identity proof, not the easy half. Detect the four
(n,k)BiPoly term-ratios (rn_num/rn_den/rk_num/rk_den) → marshal each via the rc191srmech_carrier_read_bipoly→ FIND the order-1 forced-recurrence certificate withsrmech_zeilberger(accept ONLY the WZ shape a₀(n)+a₁(n)=0 with a₀,a₁ NONZERO constants in n) → PROVE the WZ equation withsrmech_wz_verifyon the 1/a₁-rescaled certificate (x_num = cert·(a1_den/a1_num), sign-normalised; x_den = rn_den).reducibleiff the WZ equation VERIFIES — reproducingsrmech.amsc.wz_certificate.wz_certificatein C (FIND and VERIFY, per the "don't ship a partial/trivial-only shell" discipline), NOT the FIND alone. Emits the DECISION literal{"reducer":"wz_certificate","reducible":true,"row":"sigma","verified":true}/{"reducible":false,"row":"sigma"}; the Python_finish_nativerebuilds the closed-form OBJECT via the SAME reducer (_try_sigma), so native == the pureinfer, byte/structurally identical. -
srmech_infer_sigma_definite_arena_bytes(rel_len, max_terms, coeff_limbs)— a DEDICATED zeilberger-scale sizer. The zeilberger creative-telescoping scratch dominates (≈ 95 MB even at the smallest degree) and grows in BOTH the k-degree AND the coefficient-limb count, so the arena is sized on the ACTUAL coefficient limbs read from the operand (inf_bipoly_max_limbs), NOT onrel_len— which would over-size the ws to GB (measured 1.1–3.9 GB) instead of the tight ~105–240 MB. Kept SEPARATE fromsrmech_infer_arena_bytesso the cheap cyclic / gosper rows never pay the MB floor. This is the "MB-scale infer arena, NOT the invoke_tool 114 KB vtable arena" the rc191 finding named. -
Honest OPEN preserved (the no-hallucination discipline, over a real reducer). A non-WZ-summable sum comes back
reducible:falsefrom the raw C decision AND frominfer→ the honest OPEN — proven over BOTH an order-2 sum (Σ_k C(n,k)² = C(2n,n), no order-1 recurrence) AND an order-1 NON-WZ sum (Σ_k C(n,k) = 2ⁿ un-normalized, recurrence [−2,1] with a₀+a₁ ≠ 0). The C path runs the genuine zeilberger FIND + wz_verify PROVE and NEVER fabricates a reduction the verify did not certify. Bignum-safe end-to-end (a >int64 coefficient rides the decimal-string transport of the rc191 reader; verified over a 10²⁵-scaled binomial that still reduces).
#796 ROW LEDGER (rows that run in C for a bare-C host vs still-pure):
- IN C (3/7): cyclic → srmech_the_one (rc176) · sigma-gosper (indefinite) → srmech_gosper (rc176) · sigma-wz_certificate (definite) → srmech_zeilberger + srmech_wz_verify (rc192, this rc, #796 row CLOSED).
- STILL PURE (4/7 → rc193+): spectral → resonant_spectrum (a float-Mat eigensolve + a dict result — needs the rc190 float-Mat marshal + srmech_symmetric_eigendecompose + a dict serialiser: HONEST-SPLIT to the next rc, its own build) · sigma_multivar → apagodu_zeilberger (needs a 3-level TriPoly reader; the apagodu ws is 655 MB–2.2 GB for even the textbook double sums — arena-prohibitive for the infer path) · sigma_q → q_wz_certificate / q_gosper (needs a QBiPoly reader — the different q-Laurent x_low/qlen[] bridge encoding) · sigma_elliptic → elliptic_wz_certificate (the EllRatio operand is an interned symbol-table wire form, NOT a coefficient list — a fundamentally different marshal). The pure infer runs all four completely (rc103 inform-don't-limit).
No ABI change (additive C symbols only — srmech_infer_sigma_definite_arena_bytes; ABI stays 4); tools stay 403 (no new ToolEntry — infer was already composes_c, the from_bodies / cooccurrence_edges precedent); the #928 Rosetta ledger + the non-compute / owed-orchestration ceilings are UNTOUCHED (non_compute 177 = owed 10 / composes_c 108 / host_glue 15 / dev_tooling 44; debt/bignum/c_exists_unbound 0). Both C build modes clean (-Wall/-Wextra/-Wpedantic asserts-live for pytest + -DNDEBUG -Werror -Wpedantic -Wextra Release); ASan/UBSan clean over the wz row (a real reduction + a bignum reduction + both honest-OPEN branches + the entry-point NULL-arg graceful return); numpy-free. New test tests/test_infer_payoff_c_rc192.py (native == pure over reducible / bignum / order-2-OPEN / non-WZ-OPEN; genuine C engagement; honest-OPEN preserved; the dedicated arena sizer; the malformed-declines-cleanly path); tests/test_infer_c_rc176.py updated (the definite-sum wz row moved from fall-to-pure into the C-built set). Next: the remaining #796 spectral row (the float-Mat eigensolve + dict path), then the CLI (main/build_parser + the 5 add_arguments → discharges the 7 owed → CEIL 10→3).
[0.9.0rc191]¶
#796 HOST-GLUE — the NESTED exact-ℚ carrier OPERAND marshal FOUNDATION (the #796 linchpin). The bignum-safe C reader that lowers the MCP nested-ℚ wire form of the §76 "telescope" reducer operands (the exact-ℚ carriers Poly / BiPoly) into arena-backed srmech_bigint coefficient arrays — extending the rc176 srmech_infer.c inf_read_poly pattern ONE nesting level per carrier, in the REUSABLE form the rc192 srmech_infer.c wiring calls to dispatch the deferred exact #796 infer rows (sigma-definite / q / elliptic) for a bare-C host. Ships in the new c/src/srmech_carrier_marshal.c:
-
srmech_carrier_read_poly/srmech_carrier_read_bipoly(public, rc192 reuse). A COEFFICIENT is a bare integer c (den 1) OR a[num,den]2-list; each scalar is a JSON int64 OR a decimal STRING (the bignum transport). APolyis an ascending-degree LIST of coefficients; aBiPolyis a k-ascending LIST of Poly-in-n, lowered to FLAT (k-then-n) num/den arrays + a per-k length arrayklen[]+ the k-degree slot countkdeg— the exact encodingsrmech_zeilberger/srmech_wz_verifyconsume. The reader lands the operand VERBATIM (no reduce/normalise); a malformed node →SRMECH_ERR_BAD_INPUT(the Python caller runs the COMPLETE pure carrier coercer, rc103 inform-don't-limit). -
BIGNUM-SAFE (the reason the wire form is a string, not a bare literal).
srmech_json's number parser isstrtoll— a>int64literal is silently CLAMPED. So a bignum coefficient rides as a decimal STRING (srmech_bigint_from_dec, never clamped): the marshal transports a 60-digit / 2¹²⁸ coefficient EXACTLY, digit-for-digit (verified). This is why the reducer carriers cannot ride the mval int64 leaf and need thesrmech_bigintreader. -
srmech_carrier_marshal_roundtrip— the ctypes-drivable PROVER. Parse akind(Poly / BiPoly / scalar) operand →srmech_bigintarrays → re-serialise CANONICAL[num,den]JSON. The C round-trip is BYTE-for-byte the Python carrier's own coefficient viewjson.dumps(canon(input), separators=(",", ":")), INCLUDING a bignum coefficient, with a SMALL arena (srmech_carrier_marshal_arena_bytes— no MB-scale reducer scratch needed).
SCOPE FINDING (why this is a FOUNDATION rc, NOT the invoke_tool vtable reducer dispatch the task first framed). Two measured facts make the vtable the WRONG home for the §76 reducers, so this rc ships the shared OPERAND MARSHAL and rc192 wires it into the srmech_infer.c DECISION path (its arena-correct home):
1. ARENA SCALE. The reducer C kernels need MB–GB caller workspaces — srmech_gosper_ws_bound ≈ 9 MB, srmech_wz_verify_ws_bound ≈ 32 MB, srmech_zeilberger_ws_bound ≈ 470 MB — sized by srmech_infer_arena_bytes (≈ 41 MB). The invoke_tool marshalling arena is srmech_invoke_tool_arena_bytes = 256·params_len + 65536 (≈ 114 KB) and JPL Rule 3 forbids malloc, so a reducer thunk carving its ws from the vtable arena ALWAYS overflows → always defers. The reducers already run in the srmech_infer.c path (rc176 sigma-gosper), which sizes the arena correctly.
2. RESULT SHAPE. The reducer MCP results serialise (json.dumps default=repr) to Python repr() STRINGS carrying only metadata — {"num":"Poly(degree=1, exact-rational)", …} / {"order":1,"coeffs":[…],"certificate":"BiPoly(k_degree=1, exact-ℚ[n,k])"} — NOT exact-ℚ coefficient structures. The infer ROUTER emits a small DECISION literal (the rc176 form), which IS byte-reproducible.
HONEST SPLIT. Poly (1-level) + BiPoly (2-level) + the scalar (EllRatio) coefficient land this rc — the §76 Σ-row core (gosper / zeilberger / wz operands) + the elliptic entry. TriPoly (3-level, apagodu) + QPoly / QBiPoly (the q-Laurent x-low structure) split to a follow-up; the actual reducer DISPATCH is rc192's srmech_infer.c wiring over these readers.
No ABI change (additive C symbols only — ABI stays 4); tools stay 403 (no ToolEntry — the marshal is a private host-glue surface, like rc187); the #928 Rosetta ledger + the non-compute / owed-orchestration ceilings are UNTOUCHED (non_compute 177 = owed 10 / composes_c 108 / host_glue 15 / dev_tooling 44; debt/bignum/c_exists_unbound 0). Both C build modes clean (-Wall/-Wextra/-Wpedantic asserts-live for pytest + -DNDEBUG -Werror -Wpedantic -Wextra Release); ASan/UBSan clean over the Poly/BiPoly/scalar marshal round-trips (incl. bignum) + the DECLINE (malformed) path + every entry-point NULL-arg graceful return; numpy-free; no libm pulled (the carrier file). New test tests/test_invoke_tool_nested_exact_c_rc191.py (native round-trip == the Python carrier coefficient view byte-for-byte, incl. bignum + mixed int/[num,den] leaves + the sigma-definite (n,k) reducer operands + the malformed-declines contract). Next: rc192 the #796 PAYOFF — wire this nested marshal into srmech_infer.c so the exact infer rows (sigma-definite / q / elliptic) dispatch their reducer DECISION for a bare-C host.
[0.9.0rc190]¶
#810 HOST-GLUE — the FLOAT-CARRIER marshal + dispatch (Mat/Vec c_dispatched tools RUN in C). rc187 built the JSON-args↔typed-C-args marshal (bucket-(a): scalar / str / list / bytes / complex); rc188/189 wired the invoke_tool dispatch spine for the clean int / bytes / rational tools. This rc widens BOTH to the real float carriers — a composes_c widening, NOT a ledger move (invoke_tool was already discharged at rc188). Ships in c/src/srmech_mcp_marshal.c + c/src/srmech_invoke.c:
-
srmech_double_repr(v, out, cap, out_len)— the shortest-round-trip float formatter (the keystone). The pureserialise_resultisjson.dumps(serialise_native(x)), andjson.dumpsrenders a float withrepr(float)= the SHORTEST decimal that round-trips (David Gay 'r' mode), NOT%.17g. So a float-RESULT tool dispatches byte-identical ONLY if the C emits that same form.srmech_double_reprreproduces it exactly for finite doubles: the shortest%.*e(p=0..16) thatstrtod-round-trips, re-rendered fixed OR scientific per CPython'sformat_float_shortrule (scientific iffdecpt ≤ −4ordecpt > 16), with the integer-valued fixed form carrying a trailing.0(repr(5.0)=="5.0"). libm-FREE (onlysnprintf("%.*e")+strtod, both libc). A naive%gloop is WRONG here (%.1gof100.0→"1e+02", butrepr(100.0)=="100.0"); the%e+decptalgorithm is verified byte-identical torepr()/json.dumps()over 60 000+ adversarial doubles (0.1,1e16,−0.0,5.0,1/3, subnormals,100.0, raw bit patterns).mm_emit_double(compact serialiser) is upgraded to it; a non-finite double falls back to the%.17gbest-effort form (never produced by the exact tools). ABI-additive. -
The marshal now lowers the float carriers.
srmech_mcp_marshal_arg("Mat", …)builds a new realSRMECH_MVAL_MATcarrier (row-majorn_rows·n_colsf64; int/float leaves → f64 — the exactcoerce_param → Mat.from_rows(is_complex=False)shape, a genuine-complex Mat still rides the by-reference path)."Vec"is IDENTITY (the flat-list passthroughcoerce_param(_to_vec)does, ints kept).srmech_mcp_serialise_resultgains the MAT case (a nested[[…]]float array, compact) so the standalone marshal round-trip is byte-identical to Python."HV"(int/byte hypervector carrier) +np.ndarray(legacy) stay deferred (rc191). -
The invoke vtable dispatches the dense-kernel Mat/Vec ops:
(Mat,Mat)→Mat—laplacian.mat_matmul(interleaves the real operands (re,0) and rides the SAMEsrmech_dense_matmul_complexthe puremat_matmuldispatches to);(Mat,Vec)→Vec—laplacian.mat_matvec(the same kernel over a k×1 column, exactlymat_matmul(M, col));(Vec,Vec)→Mat—laplacian.mat_outer(a lone IEEE multiply per element — no accumulation / FMA, so byte-exact on every platform). The result serialises as a nested / flatLIST-of-FLOATviasrmech_double_repr(default", "separators, matchingserialise_result). Each DEFERS (never a wrong answer) on a complex-via-JSON operand, an empty / dim-mismatch / giant (>1024) shape. -
Now dispatching in C via the spine: 35 of ~180
c_dispatchedtools (rc188/189's 32 + this rc's 3). The #796 spectral row (coupling.resonant_spectrumover a floatMat) now marshals itsMatarg through the new carrier, but still DEFERS (it is not in the vtable — it needs the eigensolve reducer + thedictresult serialise; scoped for rc192/193). The remaining residue is the other float-carrier ops whose result is not byte-reproducible in a fresh thunk (eigen / svd / solve / pure-reductionmat_dot/mat_norm), theHVcarriers (rc191), and the nested-exact reducer carriers (BiPoly/TriPoly/QBiPoly/EllRatio; rc192).
No ABI change (additive C symbols only — ABI stays 4); tools stay 403; the #928 Rosetta ledger + the non-compute / owed-orchestration ceilings are UNTOUCHED (non_compute 177 = owed 10 / composes_c 108 / host_glue 15 / dev_tooling 44; debt/bignum/c_exists_unbound 0). Both C build modes clean (-Wall/-Wextra/-Wpedantic asserts-live for pytest + -DNDEBUG -Werror -Wpedantic Release); ASan/UBSan clean over the Mat/Vec marshal round-trip + the shortest-repr formatter (adversarial doubles) + the three thunks + DEFER + NULL-arg; numpy-free; no libm pulled. New test tests/test_invoke_tool_float_carrier_c_rc190.py (native == pure byte-for-byte + the srmech_double_repr == repr() adversarial sweep + the DEFER + server parity). Next: rc191 the HV carriers; rc192 the nested-exact marshal (BiPoly/TriPoly/QBiPoly/EllRatio → the 16 reducer thunks, the #796 linchpin); rc193 the #796 payoff.
[0.9.0rc189]¶
#810 HOST-GLUE — invoke_tool CLEAN BATCH 2 (the tools/call dispatch spine widens to 12 more C-backed tools). rc188 shipped srmech_invoke_tool with 20 batch-1 tools across 7 signature-shape thunks; rc189 WIDENS the same thunk vtable (c/src/srmech_invoke.c) with 12 more bucket-(a) CLEAN c_dispatched tools in new signature shapes — a composes_c widening, NOT a ledger move (invoke_tool was already discharged composes_c at rc188). Each new thunk marshals the typed argv (rc187 srmech_mcp_marshal_arg), calls the bespoke C kernel, and serialises byte-identical to the pure serialise_result(invoke_tool(...)); each DEFERS (never a wrong answer) on an out-of-domain input, an intermediate exceeding int64 (Python's bignum path), or an arity / shape mismatch.
- The 12 batch-2 tools + their shapes:
(pair,pair)→pair—rational.rational_{add,mul,div};(pair,int)→pair—rational.rational_pow_uint(exp 1..64 in C; 0 / >64 / <0 → pure);(int,int)→list[int]—rational.continued_fraction;list[int]→list[(int,int)]—rational.continued_fraction_convergents;(int,int,int)→int—primes.cyclic_period(the thunk replicates the Pythonn≥2/gcd(a mod n, n)==1/max_kdefault guards so a defer lands exactly where the pure raises);Sequence[bytes]→bytes—hdc.bundle;list[bytes]→list[str]—format.sha256_batch(hex digests);(bytes,list[(bytes,int)])→(bool,int)—dispatch.match;(bytes,list[(bytes,bytes)])→bytes|null—naming.lookup;(bytes,Mapping[bytes,bytes])→bytes—template.render(keys sorted before packing, per the wrapper's binary-search contract). - New result-serialise leaves in the batch list emitter: BOOL (
true/false), STR (JSON-quoted; ASCII-clean subset — a control / non-ASCII byte latches overflow → defer), and null. The rational/pair/list/nested-list shapes ride the existing default-separator emitter. - Now dispatching in C via the spine: 32 of ~180
c_dispatchedtools (rc188's 20 + this batch's 12). The remaining clean residue is the float-carrier (Mat/Vec/HV) tools (rc190–191), the nested-exact reducer carriers (BiPoly/TriPoly/QBiPoly/EllRatio; rc192), and the bignum-only exact ops (*_series_truncate,pi_*,crt_combine,rational_reconstruct) whose arbitrary-precision results the int64 thunk path can't hold.
No ABI change (additive C functions only — ABI stays 4); tools stay 403; the #928 Rosetta ledger + the non-compute / owed-orchestration ceilings are UNTOUCHED (owed_orchestration 10, composes_c 108). Both C build modes clean (-Wall asserts-live for pytest + -DNDEBUG -Werror -Wpedantic Release); ASan/UBSan clean over the batch dispatch + DEFER + NULL-arg paths; numpy-free. New test tests/test_invoke_tool_clean_batch2_c_rc189.py.
[0.9.0rc188]¶
#928 ORCHESTRATION→C SPINE — the tools/call DISPATCH SPINE in C (invoke_tool makes MCP tools/call genuinely RUN in C). With the rc184 registry (the 403-tool const table) + the rc187 arg-marshalling foundation (the srmech_mval_t carrier + srmech_mcp_marshal_arg / srmech_mcp_serialise_result) in place, this rc WIRES them into a dispatch spine: a bare-C host now runs a real tools/call for the C-backed common tools. Ships in the new c/src/srmech_invoke.c:
srmech_invoke_tool(name, params_json, params_len, ws, …, out_kind)— the spine:srmech_tool_registry_find(rc184) → per-argsrmech_mval_from_json+srmech_mcp_marshal_arg(rc187, keyed on the registry param type) → a SIGNATURE-SHAPE-batched thunk table (tool name → the bespoke C kernel with its exact ws-bound / out-param signature) → serialise the result. On a clean dispatch*out_kind = SRMECH_INVOKE_DISPATCHEDandbufholds the result TEXT byte-identical to the pureserialise_result(invoke_tool(name, args)); on anything the C can't handle*out_kind = SRMECH_INVOKE_DEFERand the caller runs the pure Pythoninvoke_tool(rc103 inform-don't-limit — never a wrong answer).srmech_invoke_tool_jsonis the parsed-args sibling the in-processsrmech_mcp.cpath uses (no re-serialise / double-parse).- The clean batch-1 (20 tools across 7 signature shapes) now dispatches in C end-to-end:
uN→u—cyclic.{gcd,lcm,mod_add,mod_mul,mod_pow,mod_inv,three_cycle}+cascade.cyclic_gcd+primes.next_prime(9);u→bool—primes.is_prime;uN→(int,int)—rational.best_rational;u→list[(int,int)]—primes.factor;bytes→hex str—format.sha256_bytes;…→bytes—tlv.tlv_pack/hdc.bind/hdc.permute/dispatch.mirror_pattern;bytes,bytes→int|null—hdc.hamming/search.byte_search/search.byte_search_backward. Every thunk DEFERS (never a wrong answer) on an out-of-domain / kernel-error input, a result exceeding int64, an EXTRA or missing argument, a length-mismatched byte pair, or a name not in the table. srmech_mcp.ctools/call is WIRED to the spine: whendefer_calls != 0it triessrmech_invoke_tool_jsonoverparams.name+params.arguments, and on a clean dispatch returns the newSRMECH_MCP_CALL_RESULTout_kind (the result text inbuf; the caller wraps it in the content + MPR attestation envelope with its own clock) — elseSRMECH_MCP_DEFER_CALL. The request parse tree and the invoke marshal arena share the callerws(front half parse / reserved tail invoke), never colliding.- The MCP server routes through it (native == pure).
srmech.mcp._server.MCPServer._handle_tools_calltries the C spine first (via_native.invoke_tool_c) on the DEFAULT server; a clean batch tool's result text comes from C, the 383 no-single-kernel tools defer to the pureinvoke_tool. The MPR attestation is built by Python over the SAME text either way, so the whole tools/call response is identical whether the compute ran in C or pure.
Result-text PARITY (the subtle bit). The pure serialise_result is json.dumps(serialise_native(x)) with the json.dumps DEFAULT separators (", " / ": "), NOT the compact form. For a SCALAR result (int/bool/hex-string/base64-string/null) there is no separator, so the rc187 compact srmech_mcp_serialise_result is byte-identical and is reused; only the two CONTAINER results (best_rational's pair, factor's list of pairs) carry a separator, and those go through iv_emit_spaced — a small default-separator integer-list emitter that matches serialise_result exactly (e.g. factor(360) → "[[2, 3], [3, 2], [5, 1]]").
Ledger — invoke_tool DISCHARGED owed → composes_c (CEIL 11 → 10). srmech.mcp._tools.invoke_tool earned its C peer (srmech_invoke_tool); the server dispatches a substantive clean batch through it and the pure invoke_tool is the complete fallback, so it moves owed_orchestration → composes_c (the transitive walk reaches no NOT-READY leaf). non_compute stays 177 = owed_orchestration 10 / composes_c 108 / host_glue 15 / dev_tooling 44; CEIL_NON_COMPUTE_OWED 11 → 10; python_only_debt / bignum_reference / c_exists_unbound all 0. NO ToolEntry change → tools.total stays 403; ABI stays 4 (additive symbols, no wire change to an existing function). Both C modes clean under -Werror -Wpedantic (-O2 + -DNDEBUG); ASAN/UBSAN clean (asserts-live) over the batch-1 tools end-to-end + the DEFER path + every entry-point NULL-arg graceful return + the srmech_mcp_handle CALL_RESULT/DEFER_CALL routing; JPL green; numpy-absent. New tests/test_invoke_tool_c_rc188.py (native == pure across the 20-tool batch + the defer cases + the server-level parity + the CALL_RESULT/DEFER_CALL routing + the NULL-arg contract). 6 SSOT files rc187 → rc188. The 10 owed left = serve_http_sse + the CLI grammar + make_class / run_class_method. Next: rc189 finishes bucket-(a) (the container-result widening) + rc190–191 the float carriers (Mat/Vec/HV — unlocks #796 spectral) + rc192 the #796 nested-exact marshal + reducer thunks + rc193 the #796 payoff.
[0.9.0rc187]¶
#928 ORCHESTRATION→C SPINE — the tool-call MARSHALLING FOUNDATION in C (the JSON-args↔typed-C-args value carrier). With the rc186 MCP control spine serving the lifecycle + discovery methods, this rc builds the shared marshalling foundation the rc188+ invoke_tool dispatch + the #796 nested-carrier marshal build on — a FOUNDATION rc (no tool dispatched, like rc184's registry DATA / rc181's F1 carrier). The MCP wire form is NOT self-describing (a param's TYPE comes from the rc184 registry, not the value), so marshalling is TWO-STAGE: (1) parse a JSON value tree into a tagged carrier; (2) typed-lower it keyed on the registry type string (the INVERSE of rc185's MCP_TYPE_LEXICON). Ships in the new c/src/srmech_mcp_marshal.c:
srmech_mval_t— the uniform JSON-args↔typed-C-args value carrier (generalises the rc181dsl_chain_rundv_value_ttagged union). Kinds NONE/INT/FLOAT/STR/LIST mirror rc181; BYTES (a decoded byte buffer; base64 on the wire) + COMPLEX (an(re,im)f64 pair;[re,im]on the wire) are the new typed leaves; BOOL + DICT complete faithful JSON coverage (a bool arg, a dict/object result, theMapping[bytes,bytes]object family). Depth-bounded (SRMECH_MVAL_MAX_DEPTH=6); every node aliases a caller bump arena (srmech_marshal_arena_t).srmech_mval_from_json— STAGE 1: mirror a parsedsrmech_jsontree into the raw carrier (null/bool/int/double/string/array/object).srmech_mcp_marshal_arg— STAGE 2: the C mirror ofsrmech.mcp._coercion.coerce_paramfor the bucket-(a) CLEAN families — identity (int/float/bool/str/number/dict/list + Optional/nested/ChainSpec/callable/array-acc),bytes(base64 str → BYTES; in-arena base64 decode, no malloc),complex(number|[re,im]→ COMPLEX),tuple[int,int],Sequence[bytes]/list[bytes],list[complex],list[list[complex]],Mapping[bytes,bytes],list[tuple[bytes,int]],list[tuple[bytes,bytes]]. A JSON null passes through for ANY type (coerce_param's null-first rule). A type outside bucket-(a) (the Mat/Vec/HV/np.ndarray float carriers = rc190 ©; the by-reference SpectralHandle/operator_name grammar = later;pathlib.Path, whosestr(Path)round-trip is OS-dependent —/on POSIX vs Windows\— so it is a host-runtime/OS concern, not a platform-invariant data type, and is DEFERRED to the OS-correct purecoerce_param; any unknown) →SRMECH_ERR_NOT_IMPLand the caller defers to the purecoerce_param(rc103 inform-don't-limit); a malformed wire value (bad base64, a non-[re,im]complex) →SRMECH_ERR_BAD_INPUT.srmech_mcp_serialise_result— the OUTBOUND inverse (the mirror ofserialise_native): a typed carrier → canonical JSON BYTE-IDENTICAL to CPythonjson.dumps(x, separators=(",", ":"))— insertion-order keys (NOT the sortedsrmech_json_writeform), defaultensure_ascii=Trueescaping (\uXXXX, astral as a UTF-16 surrogate pair — the rc185 em-dash idiom), bytes → base64, complex →[re,im], tuple → array. Two-passbuf==NULL ⇒ size-query/SRMECH_ERR_OVERFLOW. FLOAT/COMPLEX use thesrmech_json.c%.17g(+".0") best-effort form (full float repr-parity is the rc190 float-carrier's scope).srmech_mcp_marshal_roundtrip— the ctypes-drivable FOUNDATION ROUND-TRIP PROVER (JSON in / JSON out) composing stage-1 + marshal_arg + serialise_result; the rc187 DoD surface.
Parity proof (the DoD — no dispatch yet). For a representative set of bucket-(a) param types the C round-trip lands on the SAME canonical JSON as the pure Python json.dumps(serialise_native(coerce_param(value, type)), separators=(",", ":")) — verified byte-for-byte in tests/test_mcp_marshal_c_rc187.py across every coercer family (base64 decode/encode == Python base64 over lengths 0–19; complex [re,im] round-trips; non-ASCII strings \uXXXX-escape; Mapping[bytes,bytes] / bytes-int / bytes-bytes pair families; the identity passthroughs; null-for-any-type; NOT_IMPL for the float carriers/handles; BAD_INPUT for malformed base64/complex). Proves the carrier + marshal genuinely lower a JSON arg to a typed C value and back — the real foundation, not a stub.
NULL-arg discipline (rc715 pattern). Every marshal ENTRY function
(srmech_mval_from_json, srmech_mcp_marshal_arg, srmech_mcp_serialise_result,
srmech_mcp_marshal_roundtrip) returns SRMECH_ERR_NULL_ARG on a
contractually-NULL param BEFORE any assert runs — a runtime-checked NULL (e.g.
a two-pass size-query buf==NULL, a caller's out_len==NULL) is a handled
precondition, NOT an invariant violation, so no assert(param != NULL) guards a
param the API gracefully errors on (the JPL Rule-5 pair is met with GENUINE
invariants: arena consistency, kind-in-range, sink-starts-clean). Verified
asserts-LIVE (-O2, no -DNDEBUG) — not only Release — so the graceful
return is identical in both builds (an asserts-live abort here would be masked by
the CI Release build).
Ledger — NO move (foundation rc). No tool is dispatched this rc (the invoke_tool spine is rc188), so non_compute stays 177 = owed_orchestration 11 / composes_c 107 / host_glue 15 / dev_tooling 44; CEIL_NON_COMPUTE_OWED 11, python_only_debt / bignum_reference / c_exists_unbound all 0 — UNCHANGED (the _coercion marshallers are a PRIVATE _-tailed submodule, already understated in the ledger per the rc183 honest note). NO ToolEntry change → tools.total stays 403; ABI stays 4 (additive symbols + types, no wire change to an existing function). Both C modes clean under -Werror -Wpedantic (-O2 + -DNDEBUG); ASAN/UBSAN clean (asserts-live) over marshal_arg (each bucket-(a) family: scalar/bytes/complex/list/tuple/mapping/nested) + serialise_result round-trips + the size-query / overflow boundary + the base64 decode + every entry-point NULL-arg graceful return; JPL green; numpy-absent. 6 SSOT files rc186 → rc187. Next: the rc188 invoke_tool SPINE (registry_find → marshal → signature-shape-batched thunk table → serialise) discharging owed 11 → 10, then the float-carrier (rc190–191) + the #796 nested-exact linchpin (rc192–193) + the CLI.
[0.9.0rc186]¶
#928 ORCHESTRATION→C SPINE — the MCP-server CONTROL SPINE (the JSON-RPC protocol + stdio LOOP in C). With the rc184 registry DATA + the rc185 projection ops in place, this rc builds the C MCP server's control spine: a bare-C host (no Python) now serves the MCP lifecycle + discovery surfaces — initialize / notifications/initialized / tools/list / ping / shutdown — natively over stdin/stdout, with the MPR attestation preimage. Ships three additive C symbols in the new c/src/srmech_mcp.c + a PAL stdio surface in c/src/srmech_platform.c:
srmech_mcp_handle— the JSON-RPC 2.0 dispatch core (the C peer ofsrmech.mcp._server.MCPServer.handle). Parses one request (into a caller arena viasrmech_json_parse), dispatches onmethod, and emits the response BYTE-IDENTICAL tojson.dumps(MCPServer("srmech-mcp").handle(req), separators=(",", ":"))for initialize / tools/list / ping / shutdown — insertion-order keys (NOT the sorted-keysrmech_json_write_wsform), defaultensure_ascii=Trueescaping; tools/list embedssrmech_tool_entries_to_mcp_defsverbatim. Notifications → a no-response signal; the JSON-RPC error envelopes (-32700 parse / -32600 invalid-request / -32601 method-not-found / -32602 invalid-params) match. Two-passbuf==NULL ⇒ size-query/SRMECH_ERR_OVERFLOWcontract.srmech_mcp_build_attestation— the MPR attestation object withresponse_sha256=srmech_sha256_hexover the exact preimagetool_name \x1f "srmech <version>" \x1f result_text \x1f retrieved_at(byte-exact withsrmech.mcp._server.build_attestation, which gained an optional pinnableretrieved_at).srmech_mcp_serve_stdio— the read-frame → handle → write-frame stdio loop. Cross-platform blocking stdin via the PAL (srmech_plat_stdin_read= POSIXread(0,…)/ WindowsReadFile(GetStdHandle(STD_INPUT_HANDLE),…);srmech_plat_stdout_write=write(1,…)/WriteFile), sosrmech_mcp.ccarries no#ifdef _WIN32. EOF (a closed stdin pipe) is the deterministic terminator → clean return; the loop MUST NOT and does not hang (a Linux teardown-terminates test pipes N requests + EOF and asserts all N processed then a prompt return). All buffers are caller-supplied (no malloc). JPL-clean (≤60-line funcs, ≥2 asserts, no goto/malloc/abs/libm, bounded).
tools/call — honest split (rc187+ arc). srmech_mcp_handle routes tools/call to a defer signal (SRMECH_MCP_DEFER_CALL) when defer_calls != 0 — the Python host then runs its pure invoke_tool + attestation (the C parse arena never sees the large tool arguments) — OR, when defer_calls == 0 (the bare-C serve_stdio loop, no Python), emits an honest JSON-RPC error (inform-don't-limit). So the loop is REAL (four of five methods fully in C) while the ~403-tool arg-marshalling + dispatch tail is honestly deferred + still tracked (owed invoke_tool).
Dispatch boundary. MCPServer.handle runtime-dispatches the lifecycle + discovery methods to srmech_mcp_handle when this is the DEFAULT server (name srmech-mcp, no --filter, the stock invoke path); tools/call, a custom name / filter, or a stale/absent lib fall back to the pure path — value / byte-identical either way.
Ledger (choice A). serve_stdio moves owed_orchestration → composes_c: a bare-C host genuinely serves initialize/tools-list/ping/shutdown natively (the loop + framing run in C), so it is a real C server, not a shell; only tools/call needs the still-owed invoke_tool. CEIL_NON_COMPUTE_OWED 12 → 11, composes_c 106 → 107, non_compute 177 unchanged; python_only_debt / bignum_reference / c_exists_unbound stay 0. NO ToolEntry change → tools.total stays 403; ABI stays 4 (additive symbols, no new typedef). Both C modes clean under -Werror -Wpedantic (-O2 + -DNDEBUG); ASAN/UBSAN clean over handle (initialize/tools-list/ping/shutdown/bad-json/notification/tools-call-defer) + the piped stdio loop + attestation; JPL green (srmech_plat_has_stdio added to the Rule-5 trivial-accessor exempt list); numpy-absent. 6 SSOT files rc185 → rc186. Next: the tool-call arg-marshalling FOUNDATION (JSON-args↔typed-C-args value carrier + ~30 per-family marshallers) → the 403-tool invoke_tool dispatch vtable → the CLI.
[0.9.0rc185]¶
#928 ORCHESTRATION→C SPINE — the tool_schema PROJECTION ops in C (HOST-GLUE tier over the rc184 registry table). With the rc184 const registry DATA table + canonical serialiser in place, this rc adds the three PROJECTION ops a bare-C host needs to serve the tool surface — discharging the last 3 tool_schema/mcp owed rows. Ships three additive C symbols in c/src/srmech_tool_schema.c (all with the rc184 two-pass buf==NULL ⇒ size-query / SRMECH_ERR_OVERFLOW contract):
srmech_get_tool_schema/srmech_tool_schema_view— the whole-schema JSON. Pythonget_tool_schema()takes no filter andtool_schema_view()ISget_tool_schema().to_jsonable(), so both project the WHOLE schema; the C peers reuse the rc184 canonical (sorted-key) emitter (refactored into the sharedts_emit_schema_sortedhelper), byte-identical tosrmech_tool_schema_to_jsonand json-parsing back EQUAL toget_tool_schema().to_jsonable()(STRUCTURAL identity — dict equality is order-insensitive; the opaqueexample/smoke_test_hintpayloads are baked sorted-canonical in the const table, so the sorted form is the only byte-stable whole-schema JSON the table can produce).srmech_tool_entries_to_mcp_defs— the advertised MCP tool-definitions as a JSON array ([{name, description, inputSchema:{type:"object", properties, required}}, ...]), byte-identical tojson.dumps(list(tool_entries_to_mcp_defs()), separators=(",", ":"))(440 354 bytes, ensure_ascii=True, the" — "em-dash description join escaped—). The srmech-param-type → JSON-schema-type map + per-type wire-encoding hint are a bounded static lexicon (MCP_TYPE_LEXICON46 entries /MCP_ENCODING_HINT24 entries) mirroringsrmech.mcp._tools._TYPE_LEXICON/_ENCODING_HINTexactly (unknown type →"string", no hint — the Python default); property keys are sanitised to^[a-zA-Z0-9_.-]{1,64}$(mirrors_sanitise_property_key).
Dispatch boundary (documented choice). tool_schema_view + tool_entries_to_mcp_defs runtime-dispatch to their C peers when no profile tools are registered (the const-table snapshot IS the live srmech registry, locked by the rc184 hash-ratchet) — value-identical / byte-identical, with a name_filter / profile-tool / stale-lib / non-OK-status fallback to pure. get_tool_schema stays a PURE constructor (option (b) of the rc185 brief): dispatching its object build to C would make the rc184 hash-ratchet test circular (that test relies on get_tool_schema() as the independent Python SSoT); its C peer is proven equivalent by object reconstruction in tests/test_tool_schema_ops_c_rc185.py.
Ledger. get_tool_schema / tool_schema_view / tool_entries_to_mcp_defs move owed_orchestration → composes_c — CEIL_NON_COMPUTE_OWED 15 → 12, composes_c 103 → 106, non_compute 177 unchanged; python_only_debt / bignum_reference / c_exists_unbound stay 0. NO ToolEntry change → tools.total stays 403; ABI stays 4 (additive symbols). Both C modes clean under -Werror -Wpedantic (-O2 + -DNDEBUG); ASAN/UBSAN clean over the three ops + the size-query / n-1-overflow boundary + the type-lexicon mapping; JPL green; numpy-absent. 6 SSOT files rc184 → rc185. Next: the MCP JSON-RPC-stdio server LOOP in C, then the tool-call arg-marshalling + the 403-tool dispatch tail, then the CLI.
[0.9.0rc184]¶
#928 ORCHESTRATION→C SPINE — the C MCP-server FOUNDATION GATE (tool_schema registry DATA in C). The first rc184+ step of "build the MCP server to C": the ~403-entry srmech.amsc.tool_schema _REGISTRY (every public callable surface — name / owner / category / summary / typed params / returns / mcp_callable) crystallised as a const C data table so a bare-C host (no Python) can produce the tool registry DATA + the canonical tool_schema_sha256 attestation with no interpreter. Ships:
- The codegen
c/tools/gen_tool_registry.py(dev-time; walksget_tool_schema().toolsin the SAME order the canonical payload uses) → the checked-in generated tablec/src/srmech_tool_registry.c(JPL-clean pure data: const arrays, no dynamic init, no malloc; non-ASCII bytes as\NNNoctal so the source is ASCII-only / MSVC-safe; the 1 over-length summary hoisted into aunsigned char[]array to dodge-Woverlength-strings). - The accessors + serialiser
c/src/srmech_tool_schema.c:srmech_tool_registry_{count,get,find}+srmech_tool_schema_to_json— a bounded, allocation-free canonical serialiser (two-pass NULL-buffer size-query) whose output is BYTE-IDENTICAL to CPythonjson.dumps(get_tool_schema().to_jsonable(), sort_keys=True, separators=(",", ":"))— the DEFAULTensure_ascii=Trueform the_mcpbtool_schema_sha256is taken over (non-ASCII escaped\uXXXX, astral code points as a UTF-16 surrogate pair, keys emitted in sorted order, compact separators; theexample/smoke_test_hintdocumentation-hint payloads spliced as their already-canonical fragment). srmech_version injected fromsrmech_version()at call time (a pure version bump needs no table regen). - The hash-ratchet (THE gate)
tests/test_tool_registry_c_rc184.py:sha256(srmech_tool_schema_to_json()) == the live Python tool_schema_sha256(byte-for-byte, 408 907 bytes) — LOCKS the C table to the Python SSoT (add/change a tool without regenerating → fails), plus a codegen-idempotence drift catcher and count/name/get-by-index/get-by-name round-trips proving the table genuinely holds the whole registry (not a subset). _native.pybindssrmech_tool_schema_to_json+ the accessors (ctypes struct mirrors), hasattr-guarded (stale lib → pure path).
FOUNDATION-ONLY (honest split): the DATA table + hash-ratchet land clean this rc; the tool_schema OPS (get_tool_schema / tool_schema_view / tool_entries_to_mcp_defs owed→composes_c ledger discharge, CEIL_NON_COMPUTE_OWED 15→~12) split to rc185 — the DATA foundation is the gate they build on. NO ToolEntry change (the registry is the same tools) → tools.total stays 403; ABI stays 4 (additive symbols); non_compute 177 / CEIL_NON_COMPUTE_OWED 15 / python_only_debt / bignum_reference / c_exists_unbound 0 unchanged. Generated pure-data .c excluded from the JPL Rule ⅓ string-content regex scans (GENERATED_DATA_FILES; its summary strings are English prose that contains tokens like free(). Both C modes clean under -Werror (-O2 + -DNDEBUG); ASAN/UBSAN clean over the table walk + serialiser + get-by-name + overflow path; numpy-absent. 6 SSOT files rc183 → rc184. Next: the MCP protocol + JSON-RPC-stdio loop in C, then the 403-tool dispatch tail.
[0.9.0rc183]¶
#928 ORCHESTRATION→C SPINE — HOST-GLUE ANNEX ratchet-scaffolding (test-infra ONLY; NO C). With the bus + DSL chain interpreter now C (rc177–182 annex), the ledger walk extends one host-facing layer further: _ROOTS gains srmech.mcp + srmech.cli + srmech.llm — a bare-C host (no Python) must also serve the MCP tool surface and run the CLI dispatch grammar. The walk yields +24 new non_compute rows (4 mcp + 17 cli + 3 llm; the 2 cli.klass re-exports resolve to the already-classified srmech.dsl._class_surface pair and do NOT count as new). CEIL_NON_COMPUTE_OWED 4 → 15; non_compute 153 → 177. NO C, no compute-op change — tools.total stays 403 (infra, not ToolEntries); ABI stays 4; python_only_debt / bignum_reference / c_exists_unbound stay 0. 6 SSOT files rc182 → rc183. rc184+ build the MCP server + CLI dispatch to C and drive the owed count back down.
The +24 split by non_compute_kind (11 owed / 9 composes_c / 1 host_glue / 3 dev_tooling).
- owed_orchestration (+11) — the CLI/MCP control-grammar + dispatch a bare-C host reimplements: the 4 MCP tool-serving ops (mcp.invoke_tool / tool_entries_to_mcp_defs / serve_stdio / serve_http_sse), the 2 CLI top-level dispatch ops (cli.main.main / build_parser), and the 5 subcommand add_arguments (cli.{bus,dsl,mcp,klass,status}.add_arguments — the argparse grammar). Ledger-undercount note: the MCP owed count UNDERSTATES the real C surface — MCPServer / MCPError are CLASSES (skipped by the callable-non-class walk) and the _coercion JSON-Schema marshallers are PRIVATE (_-tailed submodule, skipped); a genuine C MCP server needs those too, and they surface as owed when built.
- composes_c (+9) — the subcommand run / run_* dispatch bodies over already-C / non_compute leaves: cli.bus.{run,run_list,run_tap,run_pipe,run_send,run_serve} (over the now-fully-C bus), cli.dsl.run (over the C DSL chain interpreter), cli.mcp.run + cli.klass.run (thin dispatch onto the owed serve/class ops). Each passes the composes_c transitive-reachability assert (reaches no python_only_debt / bignum_reference / c_exists_unbound leaf — all three buckets are 0).
- host_glue (+1) — cli.status.run reads ~/.srmech/*.ndjson (host FS) via srmech.introspect.
- dev_tooling (+3) — the whole srmech.llm surface (anthropic_agent._to_anthropic_name + anthropic_agent_cli.build_parser / main), added to NON_COMPUTE_DEV_TOOLING_EXEMPT. HONEST-DEFAULT, USER-DECISION-PENDING: llm = dev_tooling because a bare-C host does not need an Anthropic-SDK agent; this is REVERSIBLE to owed_orchestration if the user elects to build a C agent (a separate C-HTTPS/TLS Messages-API arc).
Mirrors rc170 + rc177 exactly. _ROOTS extended in the three cross-walks (test_rosetta_completeness / conftest._ROSETTA_ROOTS / test_rosetta_transitive_standalone); the 24 rows classified in rosetta_classification.ndjson; the shared count pins bumped in test_non_compute_ratchet_rc170.py + test_annex_ratchet_rc177.py (living-pin, as rc177 bumped rc170); the new test_annex_ratchet_rc183.py pins the host-glue annex specifics (the +24 split, the ceiling 15, the full split 15/103/15/44 = 177, mcp/cli/llm in every walk, numpy-absent import). Verified numpy-absent (and anthropic-SDK-absent for llm).
[0.9.0rc182]¶
#928 ORCHESTRATION→C SPINE — ANNEX Batch B part 2: the DSL CHAIN interpreter is COMPLETE. rc181 landed the FOUNDATION (F1 carrier + leaf-dispatch + build_chain_from_dict IR + LINEAR chain.run); this rc lands the loop / fold / reduce COMBINATORS completing srmech_dsl_chain_run + the TOML front-end bridge srmech_dsl_toml_chain_to_json. The four chain rows — chain (Chain.run) / run_toml_chain / build_chain_from_toml / build_chain_from_toml_str — move owed_orchestration → composes_c, CEIL_NON_COMPUTE_OWED 8 → 4. NO new public op (tools.total stays 403 — the new C symbols back the standalone-C DSL runner, not new ToolEntries); ABI stays 4 (additive symbols); python_only_debt / bignum_reference / c_exists_unbound stay 0. 6 SSOT files rc181 → rc182.
The combinators (extend srmech_dsl_chain_run.c; ABI stays 4). The build_chain_from_dict discriminator grammar now RUNS in C — a combinator stage runs a BODY over the F1 carrier by re-entering the stage-runner:
- loop {"loop_n":N,"sub_chain":[<stage>,..]} — value-threads the sub-chain N times (each iteration's output feeds the next); the loop RE-ENTERS the stage-runner for its sub-chain (mutual recursion, depth-bounded by DCR_MAX_SUBCHAIN_DEPTH = 16 — JPL Rule 1). N is BOUNDED by a compiled DCR_MAX_LOOP_N (= 2²⁴) + asserted (JPL Rule 2); a larger / negative N defers to pure.
- fold {"fold_init":<scalar>,"fold_op":<op>} — acc = fold_init; for each element of the input LIST, acc = op(acc, elem) via a C-backed BINARY body. Empty list → acc = fold_init. Sequence length bounded by DCR_MAX_SEQ (= 2²⁴) + asserted.
- reduce {"reduce_op":<op>} — acc = list[0]; fold the binary body over the rest. An empty list → non-OK (the pure functools.reduce raises ValueError; the C engine defers to it).
- binary body dispatch — cyclic_gcd is the C-backed binary op (srmech_cascade_cyclic_gcd_u64; both operands non-negative int64 = the uint64 gcd surface, matching the Python cascade.cyclic_gcd native gate). Any other body → non-OK → pure.
- parallel — the Klein-4 four-sector fan-out DEFERS to pure (it runs on host threads — a runtime affordance, inform-don't-limit, NOT a shell violation).
New C symbols — the TOML front-end bridge (srmech_dsl_chain_run.c; ABI stays 4).
- srmech_dsl_toml_chain_to_json — parse a [chain] + [[stage]] TOML chain-spec via srmech_toml_parse, then serialise the parsed table tree as canonical JSON = the build_chain_from_dict IR (TABLE→object / ARRAY→array / INT→int / FLOAT→double / BOOL→bool / STRING→string; byte-identical to json.dumps(sort_keys=True, ensure_ascii=False) for null/bool/int/string/object/array — DOUBLE best-effort %.17g, WITHIN-TOL, the chain-spec grammar is float-rare). So a C-only / MCU host reads a chain descriptor with NO Python TOML hop. A syntax error / overflow → non-OK, and the Python caller falls back to the stdlib tomllib parse (same dict / same TOMLDecodeError).
- srmech_dsl_toml_chain_to_json_arena_bytes — the caller-arena sizer.
Python dispatch. srmech.dsl.Chain now carries a parallel _native_ir (the build_chain_from_dict stage dict per stage, or None for a non-nativizable stage — a parallel fan-out, a non-scalar kwarg, a non-F1 fold seed, a non-nativizable loop sub-chain). Chain.run assembles the full chain_json (nested loop sub-chains included) and runs it in srmech_dsl_chain_run when EVERY stage nativizes; a single None → the pure combinator path. build_chain_from_toml_str routes its TOML PARSE through srmech_dsl_toml_chain_to_json (native), falling back to tomllib when native is absent / declines; build_chain_from_toml reads the file bytes (host I/O) then routes through _toml_str; run_toml_chain = build + srmech_dsl_chain_run. All hasattr-guarded → a stale lib keeps the pure runner.
Verified. New tests/test_dsl_combinators_c_rc182.py drives native == pure over the SHIPPED loop / fold / reduce chains (loop of chiral_flip incl. nested; fold / reduce of cyclic_gcd incl. seed / empty / coprime), a TOML-built chain, and run_toml_chain end-to-end, plus the C srmech_dsl_toml_chain_to_json IR == tomllib parse; + the defer paths (a parallel fan-out, a negative cyclic_gcd operand, a non-C fold body, an empty reduce) assert the C returns non-OK and the pure path runs / raises. The rc181 test_combinator_chain_defers_to_pure deferral pin is INVERTED (test_loop_fold_reduce_now_nativize). An ephemeral WSL /tmp -fsanitize=address,undefined C driver ran clean + genuinely-engaged over a loop / fold / reduce / nested-loop chain + a TOML-built chain + the parallel-defer + reduce-empty + toml-syntax-error paths. Both C build modes (-O2 / -DNDEBUG) pedantic-clean; numpy-absent.
[0.9.0rc181]¶
#928 ORCHESTRATION→C SPINE — ANNEX Batch B part 1: the DSL chain interpreter FOUNDATION → C. The srmech.dsl cascade chain interpreter — a SIBLING to the amsc.compose chain-runner — gets its C foundation: the F1 carrier-FFI + the leaf-dispatch table (lookup_cascade_op) + the build_chain_from_dict stage-IR grammar + the LINEAR chain.run value-thread over the C-backed cascade atoms. lookup_cascade_op + build_chain_from_dict move owed_orchestration → composes_c, CEIL_NON_COMPUTE_OWED 10 → 8. NO new public op (tools.total stays 403 — the new C symbols back the standalone-C DSL runner, not new ToolEntries); ABI stays 4 (additive symbols); python_only_debt / bignum_reference / c_exists_unbound stay 0. 6 SSOT files rc180 → rc181.
New C symbols — the DSL chain LINEAR run-loop (srmech_dsl_chain_run.c; ABI stays 4).
- srmech_dsl_chain_run — the C peer of srmech.dsl.Chain.run. VALUE-THREADS the F1 carrier through the chain's op stages (each stage's output feeds the next stage's input; NO @row/@input/@step refs — simpler than the compose runner). chain_json = the build_chain_from_dict dict {"chain":{"name":..},"stage":[{"op":..,<kwargs>..},...]}; input_json = an F1 value descriptor; out = an F1 value descriptor for the final value. Reuses the srmech_compose_run.c scaffold IDIOMS (forward-only bump arena, srmech_json parse/build, a tagged-union value carrier, the rc103 defer-to-pure gate) WITHOUT touching srmech_chain_run.
- srmech_dsl_chain_run_arena_bytes — the caller-arena sizer.
The F1 carrier (the shared carrier-FFI bedrock #796's F2/F3/F4 extend). A tagged union dv_value_t {NONE, INT (i64), FLOAT (f64), STR, LIST}; LIST carries an is_tuple bit (Python list vs tuple) + BOUNDED-depth children (DV_MAX_DEPTH = 6 — the nesting recursion is depth-guarded + asserted, never unbounded; JPL Rule 1). Marshalled as a canonical-JSON value descriptor: {"k":"n"} | {"k":"i","v":<int>} | {"k":"f","v":<num>} | {"k":"s","v":<str>} | {"k":"l","v":[..]} (list) | {"k":"t","v":[..]} (tuple). FLOAT round-trips at %.17g → the numeric atoms' parity is WITHIN-TOL, not byte-identical (the rc171 lesson); exact / structural stages are exact. #796's F2/F3/F4 (mat/vec/hv/complex carriers) extend it by adding new kinds to the union + new "k" tags — the union grows, the marshal contract stays.
The leaf-dispatch table (lookup_cascade_op → C kernel). The C-backed unary value → value atoms: magnitude (srmech_cascade_magnitude_f64), reorient (_reorient_i64/_f64, orientation= kwarg), pin_slot_at_zero (_pin_slot_at_zero_f64 → tuple), best_rational_signed (_best_rational_signed_f64, max_denominator=/fine_scale= → tuple), chiral_flip (_chiral_flip_i64/_f64, type-preserving), net_chirality (_net_chirality_i8), autocorrelation (srmech_autocorrelation_f64). Any other op — cyclic_gcd / chiral_dual (2-ary / higher-order), kuramoto_step / quaternion_dft / octonion_dft (heavier multi-array carriers), a user composite — → non-OK → the COMPLETE pure path (rc103 inform-don't-limit; never a wrong answer).
The honest SPLIT (Batch B pt1 vs rc182). This rc lands the FOUNDATION (F1 carrier + leaf-dispatch + build_chain_from_dict IR + LINEAR chain.run). The combinators (loop/fold/reduce completing chain.run) + the TOML front-ends (build_chain_from_toml{,_str}) are rc182 (they stay owed_orchestration); make_class / run_class_method are HONEST-DEFERRED past Batch B (leaf-op-blocked on the genome/sed_* domain-leaf C backlog, NOT object-model-blocked). A chain with a combinator stage → the C peer defers → the pure combinator path runs.
Python dispatch. srmech.dsl.Chain.run calls srmech_dsl_chain_run when HAS_NATIVE (hasattr-guarded → a stale lib keeps the pure runner); the native path is skipped while a publish context is active (so the per-stage introspection events still fire on the pure path). A non-eligible chain (empty / combinator / non-scalar kwarg), an unsupported input carrier, a non-C leaf, or a C overflow returns the native miss → the pure loop runs.
Verified. New tests/test_dsl_chain_c_rc181.py pins the surface (both symbols bound, ABI 4) + drives native == pure over shipped LINEAR chains: magnitude (scalar), chiral_flip (i64/f64 list + tuple type-preservation), net_chirality, pin_slot_at_zero + best_rational_signed (tuple), autocorrelation (WITHIN-TOL ~1e-9), reorient (orientation= kwarg), multi-stage then-threads; + the defer-to-pure paths (a .loop(...) combinator, a non-C cyclic_gcd/chiral_dual leaf, an out-of-int64 seed) all assert the C returns non-OK and the pure runs. An ephemeral WSL /tmp -fsanitize=address,undefined C driver ran clean + genuinely-engaged over the F1 carrier round-trip (scalar / list / nested / tuple) + a multi-stage linear chain + the defer path.
[0.9.0rc180]¶
#928 ORCHESTRATION→C SPINE — ANNEX Batch A part 2b: the bus pub/sub (pipe) → C — BUS FULLY C. The last owed bus row (srmech.bus._pipe.pipe) earns its C peer: owed_orchestration → composes_c, CEIL_NON_COMPUTE_OWED 11 → 10. NO new public op (tools.total stays 403 — new C symbols back the standalone-C bus, not new ToolEntries); ABI 3 → 4 (a NEW callback typedef — see below); python_only_debt / bignum_reference / c_exists_unbound stay 0. 6 SSOT files rc179 → rc180.
New PAL surface — the mutex. srmech_plat_mutex_{init,lock,unlock,destroy} (pthread_mutex on POSIX / CRITICAL_SECTION on Windows / a no-op on a thread-less target, so the serial caller path is preserved) — ONE PAL API, no #ifdef _WIN32 leaks into srmech_bus.c. The OS lock lives in the caller's storage (no heap; a _Static_assert checks the 64-byte fit per platform); it is cast in place (never memcpy'd after init) so the same stable pointer is always passed to lock/unlock (ThreadSanitizer tracks the lock by it).
New C symbols — pub/sub broadcast fan-out (ABI 3 → 4).
- srmech_bus_subscriber_callback_t (NEW typedef) — the pub/sub client's broadcast-delivery callback. Adding a callback typedef carries a ctypes CFUNCTYPE wire-format implication (the same convention that bumped v2 → v3 for the req/rep handler typedef), so ABI bumps 3 → 4 (the plain additive functions alone would not). SRMECH_ABI_VERSION / EXPECTED_ABI_VERSION updated in lockstep.
- srmech_bus_pubsub_accept — accept + register ONE subscriber into a BOUNDED (SRMECH_BUS_MAX_SUBSCRIBERS = 64) registry, mutex-guarded (spin one per thread; join before srmech_bus_server_stop, the rc179 accept model). Every server handle (from srmech_bus_serve / _serve_encrypted — the SAME handle) now carries a ready mutex + empty registry; the req/rep path just never touches them.
- srmech_bus_broadcast — fan one frame out to every active subscriber under the mutex (serialised → per-subscriber frame order preserved). On an encrypted server each subscriber has its OWN send cipher (independent packet_seq — mirrors the Python per-subscriber send_bio). A failed write DROPS that subscriber (peer closed = unsubscribe); the OS socket send buffer is the bounded per-subscriber queue (no heap growth).
- srmech_bus_subscriber_count — mutex-guarded registry-size accessor (a subscribe/broadcast sync point).
- srmech_bus_subscribe — client streams broadcasts into a caller buffer (decrypting on the encrypted path), invoking srmech_bus_subscriber_callback_t per frame; a non-OK callback return unsubscribes.
- srmech_bus_pipe — the pipe composition: subscribe source → fire-and-forget forward each broadcast to sink (identity), composing srmech_bus_connect (×2) + srmech_bus_subscribe + the frame writer. Mirrors srmech.bus._pipe.pipe (the transform= / asyncio wrappers stay Python-side affordances).
Behavior parity (mirrors Python broadcast / subscribe / pipe). A publish reaches ALL current subscribers in order; a late subscriber gets only post-subscribe messages; unsubscribe stops delivery and the server drops the dead subscriber; teardown (srmech_bus_server_stop) closes every subscriber conn + destroys the mutex, leaking nothing. Teardown joins accept threads before free (no use-after-free).
Verified. New tests/test_bus_pubsub_c_rc180.py pins the surface (5 pub/sub + 4 PAL-mutex symbols, ABI 4, the callback typedef constructible) and drives a REAL ctypes round-trip (2-subscriber ordered broadcast + late-subscriber miss + unsubscribe-drop) over the AF_UNIX PAL transport + PAL mutex registry, plus a teardown-terminates guard (a subscriber blocked mid-recv is woken by server_stop, which returns promptly). Two ephemeral WSL /tmp C drivers ran clean: (1) -fsanitize=address,undefined over multi-subscriber broadcast + late subscriber + unsubscribe/drop + pipe (leak-clean, UB-clean); (2) -fsanitize=thread (ThreadSanitizer) over concurrent registration (an accept thread) + broadcasting (the main thread) + N reading subscribers — NO DATA RACES. The rc179 pub/sub deferral pin is inverted (the C symbols now exist).
Platform scope — pub/sub is POSIX-first (Windows is a follow-up rc). The initial rc180 CI surfaced a Windows-only hang in the pub/sub-driving test (ubuntu×2 + macOS passed; windows-latest never completed). Root cause: the Windows PAL transport is a NAMED PIPE whose instance is created lazily inside accept (POSIX listen pre-binds), so (a) a client can race ahead of the lazy instance and fail to connect, leaving pubsub_accept blocked forever, and (b) a synchronous ConnectNamedPipe is not reliably woken by CloseHandle from another thread at teardown. A correct Windows pub/sub server (overlapped ConnectNamedPipe + a stop-event, or pre-created instances + a self-connect wake) needs Windows-CI verification and is a documented follow-up rc. Disposition this rc: the C path is byte-identical across platforms (POSIX behavior unchanged — ASAN/UBSAN/TSan re-verified), the pub/sub-server-driving tests SKIP on Windows (they never hang; the symbol/ABI/typedef checks still run there), and the C symbols still build on Windows. pipe stays composes_c (its C peer exists on the platforms where the server runs). The req/rep + encrypted transport (rc2 / rc179) are unaffected.
The ratchet numbers (pipe owed → composes_c — BUS FULLY C). srmech.bus._pipe.pipe owed_orchestration → composes_c; CEIL_NON_COMPUTE_OWED 11 → 10; the four sub-buckets still sum to 153 (owed 10 / composes_c 88 / host_glue 14 / dev_tooling 41). NEXT = Batch B (the nested-carrier FFI → DSL chain-interp chain / run_toml_chain / build_chain×3 / lookup → make_class / run_class_method → the 8 dsl owed rows → CEIL 10 → 2), then host-glue full C.
[0.9.0rc179]¶
#928 ORCHESTRATION→C SPINE — ANNEX Batch A part 2: the bus Bio-TOTP ENCRYPTED transport → C (a bare-C host now speaks the ENCRYPTED wire, not just plaintext). HONEST SPLIT — the cipher-wire ships; pub/sub (pipe) → C DEFERRED to rc180. NO new public op (tools.total stays 403 — new C symbols back the standalone-C bus, not new ToolEntries); ABI stays 3 (additive symbols, hasattr-guarded); python_only_debt / bignum_reference / c_exists_unbound stay 0. 6 SSOT files rc178 → rc179.
Scope investigation (SCOPE-THEN-BUILD). The standalone-C bus (c/src/srmech_bus.c) is NOT Python-dispatched — Python has its own asyncio/socket bus; the C bus is the bare-C-host mirror, exercised by C drivers. Two features were on the table: (a) wire the rc178 cipher into the C transport, (b) the pub/sub broadcast/subscribe fan-out (pipe → C). Both need a NEW PAL surface (the PAL had neither a wall clock nor a mutex): (a) needs an autonomous clock; (b) needs a mutex + a genuinely concurrent subscriber-registry + client subscribe streaming + the pipe composition under a no-data-races bar. Doing both = two new PAL surfaces + a concurrent fan-out in one rc — too big for one clean rc. Per the task's split guidance, (a) — serial req/rep, no data races — ships this rc; (b) defers to rc180.
New PAL surface — the wall clock. srmech_plat_now_ns (ISO C11 timespec_get(TIME_UTC); ONE implementation for POSIX + Windows, no #ifdef; a clock-less target returns SRMECH_ERR_IO) so the encrypted bus rolls its Bio-TOTP key window autonomously without a Python time.time_ns().
New C symbols — the encrypted transport (each additive → ABI stays 3).
- srmech_bus_serve_encrypted / srmech_bus_connect_encrypted — same as srmech_bus_serve / _connect but the transport wraps each request/response payload in the rc178 UTLP Bio-TOTP wire cipher (wire body [nonce:16][ciphertext], TLV-framed). send_recv / server_accept_one / server_stop / client_close are SHARED (they read the cipher state from the handle). ENCRYPT composes srmech_bio_totp_derive_key + _keystream_xor; DECRYPT composes srmech_bio_totp_decode_splice (permissive binding over the OPAQUE payload — the DEFAULT stdlib HMAC-SHA-256 counter-mode path; the AES-128-CTR [crypto] extra stays Python, out of the bare-C-host default). Per-connection nonce counter (a stack copy of the server config with send_seq reset), so two connections never share a packet_seq. Caller-arena + cold-path allocation only (JPL Rule 3); the key window rolls on srmech_plat_now_ns (a large window_ns pins one key). The plaintext serve / connect path is byte-for-byte unchanged (cipher disabled).
Honest limitation (documented). The C transport carries OPAQUE payloads on ONE shared host clock, so it mirrors the pure decode_splice (permissive binding, current-then-±1 window), NOT the Python bus's STRICT JSON-Event binding + replay guard — those ride the JSON-Event bus surface tracked with pub/sub for rc180.
Verified. A /tmp ASAN/UBSAN C driver runs the full encrypted round-trip end-to-end over the real AF_UNIX PAL transport + PAL threads + PAL wall clock (empty / 1 / 19 / 4000-byte payloads recover byte-exact; a wrong-DNA client does NOT recover the plaintext; the plaintext path still round-trips) — leak-clean + UB-clean, race-free teardown (one accept_one per thread, joined before server_stop). New tests/test_bus_cipher_transport_c_rc179.py pins the two new symbols + ABI 3 + WIRE PARITY (a frame the C bus builds is recovered by the Python decode_splice on the HMAC path — cross-implementation wire compatibility) + the deferral marker (pub/sub is deferred, not stubbed).
The ratchet numbers (pipe DEFERRED → no ledger move). srmech.bus._pipe.pipe stays owed_orchestration; CEIL_NON_COMPUTE_OWED stays 11; the four sub-buckets still sum to 153 (owed 11 / composes_c 87 / host_glue 14 / dev_tooling 41). The encrypted serve / connect Python ops were already composes_c, so wiring the C cipher into the transport moves no ledger row. NEXT = rc180 (pub/sub pipe → C: PAL mutex + subscriber-registry broadcast fan-out + client subscribe + pipe composition → pipe owed → composes_c, CEIL 11 → 10), then Batch B (nested-carrier FFI → DSL chain-interp → make_class).
[0.9.0rc178]¶
#928 ORCHESTRATION→C SPINE — ANNEX Batch A part 1: the bus Bio-TOTP wire cipher earns its C peer; decode_splice moves owed_orchestration → composes_c (CEIL_NON_COMPUTE_OWED 12 → 11). A USER-APPROVED defensive-parity exception to the standing "crypto is framework-reading-only" discipline — standard HMAC-SHA-256, purely defensive local-IPC, a FAITHFUL BYTE-EXACT C mirror of the already-shipped Python cipher (srmech/bus/_bio_totp.py). Nothing invented / strengthened / weakened: byte-exact parity IS the correctness + safety property. NO new public op (tools.total stays 403 — new C symbols back existing Python ops, not new ToolEntries); ABI stays 3 (additive symbols, hasattr-guarded); python_only_debt / bignum_reference / c_exists_unbound stay 0. 6 SSOT files rc177 → rc178.
New C symbols (each composes the existing kernels — no new hash, no new parser).
- srmech_hmac_sha256 — standard RFC 2104 HMAC-SHA-256 built over the srmech SHA-256 compression (incremental ipad/opad blocks → no key‖msg concatenation buffer, arbitrary key/msg length, bounded stack, JPL-clean). Byte-exact with the RFC 4231 vectors AND Python hmac.new(key, msg, sha256). A general primitive; the SHA-256 file also factored a shared FIPS-finalize helper (srmech_sha256_hex now reuses it).
- srmech_bio_totp_derive_key — sha256(dna ‖ floor(time_ns/window_ns) as BE8-signed)[0:16] (Python // floor semantics mirrored exactly).
- srmech_bio_totp_keystream_xor — the HMAC-SHA-256 counter-mode keystream XOR (encrypt == decrypt; block i = HMAC(key, nonce ‖ i_be4)).
- srmech_bio_totp_decode_splice (+ _arena_bytes) — the whole decode_splice: try windows (0, −1, +1); on the first whose decryption binds to the nonce under the PERMISSIVE rule (a srmech_json parse of attestation.sender_id_u64 / channel_id_u32), return the plaintext + used-time. Caller-arena, malloc-free.
Python dispatch (hasattr-guarded; the COMPLETE pure path is retained for a stale / no-C host). srmech.bus._bio_totp routes derive_key / the default-backend _stream_cipher / decode_splice through the C peer under has_native_bio_totp(). The C peer mirrors ONLY the DEFAULT stdlib HMAC-CTR path — the optional AES-128-CTR backend (the srmech[crypto] extra) stays Python and is bypassed (decode_splice native dispatch is gated on not _HAVE_AES_CTR); AES is out of the bare-C-host default. Verified byte-exact: an encrypt→decrypt round-trip, the RFC 4231 HMAC vectors + Python-hmac sweep, and decode_splice native == forced-pure over accept / ±window walk / binding-mismatch / wrong-key / empty / short-frame (new tests/test_bus_cipher_c_rc178.py); ASAN/UBSAN clean. The strict-binding bus consumers (test_bus*) keep identical verdicts.
The ratchet numbers. decode_splice owed_orchestration → composes_c: CEIL_NON_COMPUTE_OWED 12 → 11 (tightness assert forces exact match with the live owed count); the four sub-buckets still sum to 153 — owed_orchestration 12→11 / composes_c 86→87 / host_glue 14 / dev_tooling 41. test_non_compute_ratchet_rc170.py _EXPECTED_SPLIT + test_annex_ratchet_rc177.py (the +39 split now 9/4/12/14) re-pinned. NEXT = rc179 (wire the cipher into srmech_bus_serve / send_recv encrypted transport + pub/sub C → pipe owed → composes_c, CEIL 11 → 10), then Batch B (nested-carrier FFI → DSL chain-interp → make_class).
[0.9.0rc177]¶
#928 ORCHESTRATION→C SPINE — the ANNEX ratchet scaffolding: the everything-mirrors ledger extends to srmech.bus + srmech.dsl (CEIL_NON_COMPUTE_OWED 2 → 12). TEST-INFRA ONLY — no C, no compute-op change. A bare-C host (no Python) must run the WHOLE apparatus — including the cross-process IPC bus and the cascade-chain / class DSL interpreter. This rc TRACKS that annex surface so the ratchet covers it; the annex BUILDS (rc178+) then drive the owed count back down. NO new public op (tools.total stays 403 — the 3 bus ToolEntries decode_splice / list_endpoints / by_name were already registered + counted at import-warmup); ABI stays 3; python_only_debt / bignum_reference / c_exists_unbound stay 0. 6 SSOT files rc176 → rc177.
Extend the ledger walk (_ROOTS) to bus/dsl — the +39 rows. tests/test_rosetta_completeness.py::_ROOTS (and the mirrored conftest.py::_ROSETTA_ROOTS shared non_compute walk + test_rosetta_transitive_standalone.py::_ROOTS) gain srmech.bus + srmech.dsl. The walk surfaces exactly 39 new public NON-class callables (18 bus + 21 dsl, keyed canonically by <module>.<qualname>; classes/constants excluded, underscore submodules skipped as iteration targets but their __all__-re-exported functions enumerated; bus/aio.py — the one non-underscore submodule — adds the 4 asyncio wrappers). All 39 are non_compute; the split by non_compute_kind:
- owed_orchestration (+10) — genuine control/dispatch LOGIC a bare-C host needs: the bus Bio-TOTP cipher stream kernel decode_splice + pipe, and the DSL chain / class interpreter chain / run_toml_chain / lookup_cascade_op / build_chain_from_{dict,toml,toml_str} / make_class / run_class_method. (decode_splice is a stream-cipher kernel but stays non_compute/owed, NOT python_only_debt — classing it compute-debt would break the CEIL_PYTHON_ONLY_DEBT=0 invariant; rc178 lands its srmech_bio_totp C peer.)
- composes_c (+3) — connect / serve / channel_id_from_name (compose the existing C sha256_bytes / cipher backend; each passes the transitive-reachability assert — reaches no non-standalone-ready ledger leaf).
- host_glue (+12) — the bus discovery / transport / registry-read surface (list / list_endpoints / by_name / new_correlation_id / bus_dir / is_posix_uds / transport_kind) + the DSL catalog/class descriptor FS loaders (load_catalog / load_class_catalog / load_chain_toml / get_descriptor / get_class_descriptor).
- dev_tooling (+14, added to the pinned NON_COMPUTE_DEV_TOOLING_EXEMPT allowlist) — a bare-C host never needs it: the bus cipher_backend_name / secret_kwargs + the 4 asyncio aio.* wrappers, and the DSL registry/introspection surface (register_catalog_dir / register_class_dir / list_cascade_ops / list_classes / list_catalog_ops / list_ops / describe_class / list_class_surface).
The ratchet numbers. CEIL_NON_COMPUTE_OWED 2 → 12 (the tightness assert forces exact match with the live owed count); NON_COMPUTE_DEV_TOOLING_EXEMPT += 14 keys (27 → 41; the pinned-allowlist test must equal the live dev_tooling set exactly); the four sub-buckets now sum to 153 (114 + 39): owed_orchestration 2→12 / composes_c 83→86 / host_glue 2→14 / dev_tooling 27→41. test_non_compute_ratchet_rc170.py _EXPECTED_SPLIT + _TOTAL_NON_COMPUTE re-pinned; new tests/test_annex_ratchet_rc177.py pins the +39 split, the ceiling, the four totals, and bus/dsl in every ledger walk. bus + dsl import cleanly numpy-absent. NEXT = Batch A (bus Bio-TOTP cipher: rc178 srmech_hmac_sha256 + srmech_bio_totp → decode_splice; rc179 wire-into-bus + pub/sub → pipe), then Batch B (nested-carrier FFI → DSL chain-interp → make_class).
[0.9.0rc176]¶
#928 ORCHESTRATION→C SPINE, BATCH 6 — the F929 dispatch.infer ROUTER earns a C peer; the CARRIER-FFI foundation lands (CEIL_NON_COMPUTE_OWED 3 → 2). srmech.amsc.dispatch.infer (the F929 OPEN/infer meta-dispatcher — the 14 A–N classes AS a DISPATCH TABLE over closed-form reduction theories) moves non_compute_kind owed_orchestration → composes_c via the new srmech_infer C router. NO new public op (tools.total stays 403 — infer gaining a C path, not a new ToolEntry); ABI stays 3 (additive symbols srmech_infer + srmech_infer_arena_bytes, hasattr-guarded); python_only_debt / bignum_reference / c_exists_unbound stay 0. 6 SSOT files rc175 → rc176.
The CARRIER-FFI foundation — the SMALLEST SOUND scope (a genuinely uncertain multi-carrier arc, split honestly). infer's relationship payloads carry LIVE carrier objects (BiPoly / TriPoly / QPoly / QBiPoly / EllRatio / Mat / the One) that expose NO persistent C handle — each marshals its state PER-OP-CALL to flat srmech_bigint / float arrays. rc176 builds the router for the two EXACT-SYMBOLIC bignum-carrier rows that share ONE carrier-FFI marshal (JSON with bignum-decimal-string coefficients — the srmech_chain_run precedent):
- cyclic (sigma / theta_num / theta_den) → srmech_the_one; VERIFY the n1_is_sigma_only invariant read from the reducer's ACTUAL output (flat[1] == (sigma, 1); the (1,3,7,3)/(0,1,3) partition invariants are structural constants). reducible iff sigma ∈ {+1,−1} AND theta_den > 0 AND the n=1 imaginary entry == (sigma, 1).
- sigma-gosper (term_ratio_num / term_ratio_den as ascending [num, den] rational coefficient lists) → srmech_gosper; VERIFY = a hypergeometric antidifference exists (has == 1).
srmech_infer parses the relationship JSON (srmech_json), DETECTS the row from the marshalled operand structure, DISPATCHES the C reducer + VERIFIES its own contract, and emits the DECISION {"reducible":.., "row":.., "reducer":.., "verified":true} (or {"reducible":false, "row":..}). The Python caller reconstructs the closed_form OBJECT from the SAME reducer this op verified, so the native path is byte-identical to the pure infer (a bare-C host reads the decision + calls the reducer for the form). ONE caller arena ws sized with srmech_infer_arena_bytes(rel_len, max_terms) — the gosper ws grows super-linearly in the term-ratio DEGREE, so the arena is sized on the ACTUAL coefficient count (not on bytes), exactly as _native.gosper_c sizes ws. Caller-arena / malloc-free / JPL-clean (≤60-line funcs, ≥2 asserts, no goto/abs/libm); pedantic -Werror -Wpedantic clean in BOTH -O2 (asserts live) and -DNDEBUG; ASAN/UBSAN clean over every built row + OPEN + fall-to-pure with each C path genuinely engaged.
rc103 inform-don't-limit (the honest split + the no-hallucination discipline). The 5 HEAVIER-carrier rows — the definite-sum wz (BiPoly), spectral (Mat + a Λ²==L·L verify), multivariate (TriPoly), q (QBiPoly), and elliptic (EllRatio) — each need their own carrier bridge + result marshalling, a multi-rc arc → rc177+. Those relationships are NOT marshalled (the Python marshaller returns None) so the COMPLETE pure infer runs them (they still work, via the reducers' own C peers). The C router NEVER returns a false reducible — a bad-σ cyclic + a non-summable gosper both come back reducible:false (the executable no-magic-numbers / no-hallucination discipline in C). New tests: tests/test_infer_c_rc176.py (23 — native==pure parity over every row class, genuine C engagement for the built rows, inform-don't-limit fall-to-pure, never-a-false-reducible). The carrier-FFI marshal (bignum-decimal-string JSON for Poly/int operands) is the SHARED foundation the DSL make_class / loop / fold interpreter (the annex) will reuse. After rc176 the only remaining owed_orchestration rows are the tool_schema pair (get_tool_schema / tool_schema_view, built with the host-glue MCP server).
[0.9.0rc175]¶
#928 ORCHESTRATION→C SPINE, BATCH 5 — the amsc.catalog CHAIN ORCHESTRATION earns C; a bare-C host lists + runs a catalog's named chains (CEIL_NON_COMPUTE_OWED 5 → 3). The two chain-runner-dependent catalog ops (list_catalog_chains + run_catalog_chain) move non_compute_kind owed_orchestration → composes_c, each COMPOSING the rc173 chain PARSE + the rc174 chain-RUNNER over the descriptor's [catalog].operator_chain. NO new public op (tools.total stays 403 — existing surfaces gaining a C path); ABI stays 3 (additive symbols srmech_catalog_list_chains / srmech_catalog_run_chain + their _arena_bytes); python_only_debt / bignum_reference / c_exists_unbound stay 0. 6 SSOT files rc174 → rc175.
The 2 C peers (compose the EXISTING kernels — NO new parser, NO new math).
- srmech_catalog_list_chains — list_catalog_chains: reuses the rc173 co_build_chain validation, then PROJECTS each normalized spec to its summary {classes:[class_id,…], n_steps, name, on_error, returns, summary} and emits the array as canonical JSON (byte-identical to the pure projection; asymptotic_calculus's 5 chains list in C).
- srmech_catalog_run_chain — run_catalog_chain: linear-scans operator_chain for the chain named chain_name (the catalog resolve), then RUNS it via the rc174 chain-runner (the shared cr_run_and_write — same bounded Class-N op set + value-descriptor OUTPUT contract as srmech_chain_run). A chain not found / an out-of-table op / a non-i64 referenced input / overflow → non-OK, and the Python caller runs the COMPLETE pure path (the not-found KeyError, or the run over the live object graph).
HONEST SPLIT — dispatch.infer (the F929 OPEN/infer router) is DEFERRED to rc176. It is NOT thin: its relationship payloads carry LIVE non-JSON carrier objects (BiPoly / TriPoly / QPoly / QBiPoly / EllRatio / Mat / the One), and moving its detect-row + try-and-verify LOGIC to C needs a full multi-carrier FFI marshalling layer for the 7 reducer families PLUS closed_form return marshalling — a multi-rc arc, not one clean rc (per the no-partial-ship / no-trivial-only-router discipline). This rc pins that infer STILL works unchanged (pure) and has NO srmech_infer C symbol (the deferral is real).
Parity + safety. Both peers are FLOAT-FREE, malloc-free / caller-arena (JPL-clean: ≤60-line functions, ≥2 asserts, no goto/recursion/abs/libm), pedantic -Werror -Wpedantic in BOTH -O2 and -DNDEBUG, ASAN/UBSAN-clean (a throwaway driver exercised list over a multi-chain catalog + run find-and-run of a threaded rational chain / pi-str / @row-pow / unknown-chain / out-of-table-op / malformed / bad-version / mid-chain-div0 → no memory/UB errors, then deleted). Dispatch is under HAS_NATIVE (ctypes, hasattr-guarded → a stale ABI-3 lib keeps the COMPLETE pure path). The srmech_chain_run run+marshal tail was refactored into the shared cr_run_and_write helper (byte-behavior identical) so both entry points reuse it.
The ledger move. In tests/rosetta_classification.ndjson the 2 catalog chain-orchestration rows move owed_orchestration → composes_c; bucket stays non_compute. CEIL_NON_COMPUTE_OWED 5 → 3 (tight: live owed_orchestration == ceiling). The split stays 3 + 82 + 2 + 27 = 114 (owed_orchestration 5→3, composes_c 80→82). The 3 remaining owed = dispatch.infer (→ rc176) + the 2 tool_schema rows (get_tool_schema / tool_schema_view → built with the host-glue MCP server). New tests (tests/test_catalog_chain_infer_c_rc175.py): every shipped catalog chain's C-orchestrated run == fully-pure (no-C-anywhere) AND == its attested (expected_num, expected_den) incl. the 9-step Friedmann bignum-ℚ chain; GENUINE-engagement (_run_catalog_chain_native / _list_catalog_chains_native return non-MISS/non-None for every shipped case); the 5-chain list projection; the inform-don't-limit MISS cases (out-of-table op, non-i64 input, non-raise policy); unknown-chain / unknown-source raising KeyError; and the infer-deferral pins (no C symbol, still routes each row + honest OPEN).
[0.9.0rc174]¶
#928 ORCHESTRATION→C SPINE, BATCH 4 — the amsc.compose chain-runner RUN LOOP earns C; the WHOLE shipped apparatus runs end-to-end in C (CEIL_NON_COMPUTE_OWED 7 → 5). The two srmech.amsc.compose run ops (resolve_chain + run_chain) move non_compute_kind owed_orchestration → composes_c: a bare-C host (no Python) now RUNS a validated [[catalog.operator_chain]] end-to-end and reaches the final value — the shipped pi-digit / asymptotic-calculus-series / Friedmann-dark-fraction chains all run in C. NO new public op (tools.total stays 403 — an existing surface gaining a C path); ABI stays 3 (additive symbols srmech_chain_run / srmech_chain_run_arena_bytes); python_only_debt / bignum_reference / c_exists_unbound stay 0. 6 SSOT files rc173 → rc174.
PARITY IS ON OUTPUT, NOT THE CLOSURE. The Python resolve_chain returns a live closure over the object graph (importlib + getattr(module, op)(**args)); a bare-C host has neither, so that is NOT mirrored. Instead srmech_chain_run RUNS the chain in C and marshals the FINAL value back as a canonical value DESCRIPTOR ({"k":"s","v":"3.14159"} | {"k":"q","n":"..","d":".."} | {"k":"i","v":".."} | {"k":"n"}; bignums as decimal strings) that the Python caller reconstructs — byte-identical to the pure path.
The C RUN LOOP (srmech_compose_run.c; composes the EXISTING kernels — NO new math). (1) A uniform VALUE CARRIER — a tagged union (int / str / bignum-rational / list / none) whose bignums alias into the caller arena. (2) A DISPATCH TABLE over the BOUNDED shipped-chain op set (all Class N): pi_cascade_digits → srmech_pi_archimedes (str; depth/precision auto-scaled the same as rational.pi_cascade_digits); {exp,sin,cos,log1p,atan}_series_truncate → the srmech_*_series_truncate_big bignum peers; rational_pow_uint → srmech_rational_pow_uint_big; rational_{add,mul,div} → a bignum-ℚ add/mul/div composed from srmech_bigint (the bigexp common-denominator + gcd-reduce pattern, positive-den reduced — byte-identical to bigq_*_c). (3) Argument resolution — each @row.<path> / @input.<path> / @step[N].output reference resolves to a value carrier (@step reads the persisting prior-step output; @catalog is NOT supported → pure). ONE caller arena, bump-allocated FORWARD (each step's op scratch carved + abandoned, each step output persists); sized by srmech_chain_run_arena_bytes.
rc103 inform-don't-limit (the C peer ALWAYS gives a byte-identical answer or none). The peer produces a result ONLY when EVERY step succeeds; ANY op outside the table, any @catalog ref, any non-"raise" error policy, any referenced non-int64 input, any float / unsupported arg, or any domain error / overflow → non-OK, and the caller runs the COMPLETE pure path (which returns the value or raises the exact ChainSpecError / ValueError / ZeroDivisionError). The Python dispatch gates on the same preconditions (all-Class-N in-table ops, raise policy, referenced ints fit int64 — UNREFERENCED bignum row columns like expected_* are irrelevant). FLOAT-FREE, malloc-free / caller-arena (JPL-clean: ≤60-line functions, ≥2 asserts, no goto/recursion/abs/libm), pedantic -Werror -Wpedantic in BOTH -O2 and -DNDEBUG, ASAN/UBSAN-clean (an 8-byte-alignment fix on the writer arena the emit-frame stack requires). Dispatches under HAS_NATIVE (ctypes, hasattr-guarded → a stale ABI-3 lib keeps the COMPLETE pure path).
The ledger move. In tests/rosetta_classification.ndjson the 2 compose run rows move owed_orchestration → composes_c; bucket stays non_compute. CEIL_NON_COMPUTE_OWED 7 → 5 (tight: live owed_orchestration == ceiling). The split stays 5 + 80 + 2 + 27 = 114 (owed_orchestration 7→5, composes_c 78→80). Next (rc175): run_catalog_chain / list_catalog_chains / dispatch.infer become buildable now the run loop landed. New tests (tests/test_chain_run_c_rc174.py): every SHIPPED catalog-chain row native == forced-pure (pi digits / the five series / the 9-step Friedmann bignum-ℚ chain, verified against its attested (expected_num, expected_den)); GENUINE-engagement (_run_chain_native returns non-MISS for every shipped row — never a silent all-pure run); hand-built chains exercising every value-carrier type + each reference kind (@row / @input / @step) + rational_pow/div threading + single-step; and the fall-to-pure cases (out-of-table op, @catalog, warn_return_none / skip, non-i64 referenced input, unreferenced-bignum-row-field-still-runs, mid-chain division-by-zero raising identically, unknown op → ChainSpecError) + the run_catalog_chain / list_catalog_chains consumers.
[0.9.0rc173]¶
#928 ORCHESTRATION→C SPINE, BATCH 3 — the amsc.compose chain-runner PARSE + VALIDATE half earns C (CEIL_NON_COMPUTE_OWED 9 → 7); the RUN loop is an HONEST SPLIT to rc174. The two srmech.amsc.compose parse ops move non_compute_kind owed_orchestration → composes_c — a bare-C host (no Python) now PARSES + VALIDATES an operator-chain descriptor's [[catalog.operator_chain]] blocks. NO new public op (tools.total stays 403); ABI stays 3 (additive symbols); python_only_debt / bignum_reference / c_exists_unbound stay 0. 6 SSOT files rc172 → rc173.
THE OP-INVOKE FINDING (STEP 1 — why the split). run_chain (via resolve_chain) does NOT invoke the bounded ~10 cascade-catalog atoms (those are a SEPARATE srmech.dsl surface over the [cascade] TOML descriptors). It resolves each step's class (A–N) → DEFAULT_CLASS_REGISTRY module → getattr(module, op)(**resolved_args) — i.e. ARBITRARY srmech ops across all 14 class modules, kwargs-dispatched by name, returning arbitrary Python objects. The shipped executable chains prove it: pi_cascade_digits (Class N, str), sha256_bytes (Class A, str), mod_add/mod_mul/gcd/mod_inv (Class I, int), {exp,sin,cos,log1p,atan}_series_truncate (Class N, tuple[int,int]). Heterogeneous signatures, resolved against the LIVE Python object graph (runtime row / inputs / prior-step outputs). So there is NO small op→C-fn dispatch table; the run loop needs a bounded-op FFI + a uniform value carrier — a multi-part foundation, scoped rc174 (not half-built here, per the no-partial-ship discipline).
The 2 C peers (each COMPOSES the srmech_json parser / builder / canonical writer — NO new parser, NO new math).
- srmech_chain_spec_parse — parse_chain_spec: validates one chain block (required name/summary/returns/steps; steps a non-empty list; class ids A–N; chain + per-step on_error in {raise, warn_return_none, skip}; every @<row|input|step|catalog>.<path> reference grammar-checked with @step[N] bounded to N < the step index) and emits the normalized {name, on_error, returns, steps:[{class_id, on_error, op}], summary} as canonical JSON.
- srmech_chain_catalog_parse — parse_catalog_chains: validates {chain_schema_version:1, operator_chain:[…]} and emits [spec, …], composing the single-chain validator.
The args-identity design. args are OMITTED from the C output; the Python caller re-attaches each step's args from the ORIGINAL dict (dict(raw_step["args"])), so arg object identity + type are byte-identical to the pure path (a tuple arg stays a tuple — never a JSON round-trip that would flatten it to a list). On ANY validation failure or non-JSON input the peer returns non-OK → the caller runs the COMPLETE pure path (which raises the specific ChainSpecError; value-parity, never a rescue). Both peers are FLOAT-FREE, malloc-free / caller-arena (JPL-clean: ≤60-line functions, ≥2 asserts, no goto/recursion/abs/libm), pedantic -Werror -Wpedantic in BOTH -O2 and -DNDEBUG. The Python ops dispatch under HAS_NATIVE (ctypes, hasattr-guarded → a stale ABI-3 lib keeps the COMPLETE pure path).
DEFERRED (stay owed_orchestration). resolve_chain + run_chain (the RUN loop, rc174) and their two amsc.catalog dependents (list_catalog_chains / run_catalog_chain) keep their owed rows — no stub shipped.
The ledger move. In tests/rosetta_classification.ndjson the 2 compose parse rows move owed_orchestration → composes_c; bucket stays non_compute. CEIL_NON_COMPUTE_OWED 9 → 7 (tight: live owed_orchestration == ceiling). The split stays 7 + 78 + 2 + 27 = 114 (owed_orchestration 9→7, composes_c 76→78). New tests (tests/test_chain_runner_c_rc173.py): native == forced-pure across a valid sweep (single / multi-step, every reference kind @row/@input/@step/@catalog, dotted-after-index, nested-args refs, chain + step error policies, skip, all A–N classes, multi-digit step index), the arg-object-type-preserved check, and every validation-failure case (unknown class, missing key, empty steps, illegal on_error, malformed / self / forward reference, non-dict, bad namespace) proving native defers to the identical pure ChainSpecError; plus the real pi_digits / asymptotic_calculus descriptors + the catalog.list_catalog_chains consumer round-tripping identically.
[0.9.0rc172]¶
#928 ORCHESTRATION→C SPINE, BATCH 2 — the amsc.catalog REGISTRY / KERNEL-STATE / AUDIT logic earns C; the chain-runner's foundation (CEIL_NON_COMPUTE_OWED 15 → 9). Six srmech.amsc.catalog ops move non_compute_kind owed_orchestration → composes_c — a bare-C host (no Python, no json.dumps) now runs the catalog registry / kernel-state / audit surface. NO new public op (tools.total stays 403 — existing ops gaining a C path); ABI stays 3 (additive symbols); python_only_debt / bignum_reference / c_exists_unbound stay 0. 6 SSOT files rc171 → rc172.
The C REGISTRY DESIGN (option a — caller-owned state). The Python catalog is stateful (module dicts: registered roots, the T2 local-kernel path + adapter-class). The C mirror keeps NO global mutable state and NO long-lived handle — the registry / kernel state is owned by the host and passed in per call (Python passes its module globals; a bare-C host passes its own struct's contents), realized as stateless arena-backed logic peers (the rc171 stateless-peer pattern). Each peer parses the caller state via srmech_json, runs the genuine LOGIC (prepend the host root, the cache-hash derivation, the NDJSON line-iterate + attestation projection, the response construction), and writes canonical JSON (byte-identical to json.dumps(obj, sort_keys=True, ensure_ascii=False)) into the caller arena.
The 4 new C peers (each COMPOSES the existing kernels — the srmech_json parser / canonical writer / builder + srmech_sha256_hex Class A; no new parser, no new hash).
- srmech_catalog_registered_roots — list_registered_roots: builds [{path, source}, …] with srmech's own attested root FIRST, then every external (path, source) pair.
- srmech_catalog_local_kernel_state — get_local_kernel_state: assembles the state envelope + the Class-A cache_hash = sha256("\n".join(f"{source_key}\t{overlay_sha256}")) over the caller-provided (FS-derived) overlay set.
- srmech_catalog_use_local_kernel — use_local_kernel + clear_local_kernel (via use_local_kernel(None)): the two reproducible HAPPY-PATH responses (the T2-cleared response + the success response for a validated overlay dir, incl. the adapter_class scope message). The error responses (invalid class / missing / not-a-dir) stay in Python — their repr-formatted messages are host presentation.
- srmech_catalog_attestation_audit — attestation_audit: iterates the NDJSON file bytes (lstrip each line; skip empty / # comment lines — the read_ndjson discipline), parses each row, and projects data_schema_id + attestation.{response_sha256, retrieved_at, parser_version, parser_rule_hash, collector_descriptor_hash} (each "" when absent — the data-only literature_curated case). A per-line parse failure returns non-OK → the caller runs the COMPLETE pure path (which raises MPRValidationError on a genuinely malformed line).
The sixth op, list_attested_sources, is classified composes_c directly (no new symbol) — it is a thin compose over the descriptor parse + a pure filter/sort/project, consistent with the already-composes_c get_attested_dataset / get_attested_descriptor. Each peer is FLOAT-FREE by construction (the projected fields are strings; the audit never touches the data block, so no float round-trips), malloc-free / caller-arena (JPL-clean: ≤60-line functions, ≥2 asserts, no goto/recursion/abs/libm), pedantic -Werror -Wpedantic in BOTH -O2 and -DNDEBUG. The Python ops dispatch under HAS_NATIVE (ctypes, hasattr-guarded → a stale ABI-3 lib keeps the COMPLETE pure path; value-parity, never a rescue).
DEFERRED (stay owed_orchestration). list_catalog_chains + run_catalog_chain compose the amsc.compose chain-runner, which is NOT in C yet (rc173–175, the hard knot) — they keep their owed rows with an # rc173+ chain-runner dep note; no stub is shipped.
The ledger move (the ratchet mechanic). In tests/rosetta_classification.ndjson the 6 catalog rows move owed_orchestration → composes_c; bucket stays non_compute. CEIL_NON_COMPUTE_OWED 15 → 9 (tight: live owed_orchestration == ceiling). The split stays 9 + 76 + 2 + 27 = 114 (owed_orchestration 15→9, composes_c 70→76). The test_non_compute_composes_c_is_transitively_reachable assert now covers the 6 (they reach no python_only_debt / bignum_reference / c_exists_unbound leaf — all 0). New tests (tests/test_catalog_c_rc172.py): native == forced-pure across register-a-root + list, a Class-E srmech_catalog_lookup hit + miss, a kernel use/get/clear round-trip (with + without an adapter-class scope), attestation_audit over every real attested source + a synthetic full-MPR NDJSON via the C peer directly, and the edge cases (empty registry, duplicate register, inactive kernel, unknown source key).
[0.9.0rc171]¶
#928 ORCHESTRATION→C SPINE, BATCH 1 — the amsc.op_provenance verdict/carry LOGIC earns C; the first owed-orchestration op-family to get a C path (CEIL_NON_COMPUTE_OWED 20 → 15). The five srmech.amsc.op_provenance verdict/carry ops move non_compute_kind owed_orchestration → composes_c — each now dispatches its provenance LOGIC (not a Python-shimmed stub) to a new C peer, so a bare-C host (no Python, no json.dumps) builds + compares op-provenance records. NO new public op (tools.total stays 403 — these are existing ops gaining a C path); ABI stays 3 (additive symbols); python_only_debt / bignum_reference / c_exists_unbound stay 0. 6 SSOT files rc170 → rc171.
The 5 C peers (each COMPOSES the existing kernels — no new parser, no new hash: the srmech_json parser / canonical writer / builder, srmech_sha256_hex Class A, and the rc117 srmech_op_provenance_hash canonical chain hasher).
- srmech_op_verdict — op_verdict's EQUAL/UNKNOWN over the canonical chain hash: hashes both records via srmech_op_provenance_hash, compares the 64-hex digests (never a false UNEQUAL; program-equality is undecidable).
- srmech_family_verdict — family_verdict's SAME_TARGET/UNKNOWN over the family ADDRESS: parses both records, compares the "family" object's target_id (non-empty + equal) AND tower_kind (both-absent counts as equal).
- srmech_op_carry — carry's RECORD (the provenance face; the numeric value stays the runner's job): hashes the canonical inputs into input_sha256 (sorted-key order), derives leaves_exact (an iterative tree scan for the __float64__/__complex128__ inexact tags — matches Python _canon's exact flag, incl. NESTED leaves), and appends chain_sha256.
- srmech_lossy_projection_record — the rc125 exact-in/exact-out LOSSY-PROJECTION record (family=null, params={}, rung={}, a projection_kind, derived leaves_exact + chain).
- srmech_op_reproject — reproject's MPM RE-VERIFICATION: do the supplied canonical inputs re-hash (sorted order) to the record's input_sha256 element-for-element? (the compute part; the rung-merge + re-run stay Python, and the re-run's record build is itself now srmech_op_carry).
Each is FLOAT-FREE by construction (a raw JSON float in any input → SRMECH_ERR_BAD_INPUT, the same domain rejection as srmech_op_provenance_hash), malloc-free / caller-arena (JPL-clean: ≤60-line functions, ≥2 asserts, no goto/recursion/abs/libm), pedantic -Werror -Wpedantic in BOTH -O2 and -DNDEBUG. The Python ops dispatch under HAS_NATIVE (ctypes, hasattr-guarded → a stale ABI-3 lib keeps the COMPLETE pure path; value-parity, never a rescue).
The ledger move (the ratchet mechanic). In tests/rosetta_classification.ndjson the 5 op_provenance rows move owed_orchestration → composes_c (they now compose existing C symbols, bottoming out in srmech_op_provenance_hash / srmech_json / srmech_sha256_hex); bucket stays non_compute. CEIL_NON_COMPUTE_OWED 20 → 15 (tight: live owed_orchestration == ceiling). The split stays 15 + 70 + 2 + 27 = 114 (owed_orchestration 20→15, composes_c 65→70). The test_non_compute_composes_c_is_transitively_reachable assert now covers the 5 (they reach no python_only_debt / bignum_reference / c_exists_unbound leaf — all 0). New tests (tests/test_op_provenance_c_rc171.py): native == forced-pure across the carry ops (interior/edge/float-leaf towers), lossy record (exact / bigint / rational / complex / NESTED-float / empty), op_verdict EQUAL/UNKNOWN, family_verdict SAME_TARGET/UNKNOWN/None, reproject verbatim/override/mismatch-raises — plus a guard that the native peers are genuinely EXERCISED (return non-None) under HAS_NATIVE.
[0.9.0rc170]¶
#928 RATCHET SCAFFOLDING — the ORCHESTRATION→C phase driver: split the 114 non_compute rows into 4 honest sub-buckets + a down-only CEIL_NON_COMPUTE_OWED ceiling. Test-infrastructure ONLY — NO compute op changes, NO C changes, NO new public op (tools.total stays 403; ABI stays 3; python_only_debt / bignum_reference / c_exists_unbound stay 0). With the compute (CEIL_PYTHON_ONLY_DEBT=0), exact-algebra (CEIL_BIGNUM_REFERENCE=0) and self-hosting (CEIL_C_EXISTS_UNBOUND=0) arcs all CLOSED, non_compute (114 rows) was the ONLY bucket with no ceiling — the honest next frontier. The phase goal: make a bare-C host (no Python) run the WHOLE apparatus — dispatch, catalogs, IPC, the genome, the chain-runner — in C. This rc builds the RATCHET that drives it, mirroring how CEIL_BIGNUM_REFERENCE drove the Qalg-C tail. 6 SSOT files rc169 → rc170.
The four-way split (a partition of the 114; each non_compute row gains a non_compute_kind field in tests/rosetta_classification.ndjson, keeping bucket:"non_compute").
- owed_orchestration (20) — genuine control/dispatch LOGIC a bare-C host needs, owed-C: the chain-runner compose.{parse_chain_spec,parse_catalog_chains,resolve_chain,run_chain} (4); the op-provenance verdicts op_provenance.{carry,op_verdict,family_verdict,reproject,lossy_projection_record} (5); the catalog register/lookup/iter/audit/kernel-state LOGIC (8: attestation_audit,list_catalog_chains,run_catalog_chain,clear_local_kernel,get_local_kernel_state,list_attested_sources,list_registered_roots,use_local_kernel); the F929 dispatch.infer meta-router (1); and the tool_schema.{get_tool_schema,tool_schema_view} op-schema LOOKUP (2 — OWED because the user chose a FULL-C MCP server, which must introspect the C tool surface from a bare-C host).
- composes_c (65) — thin: already composes existing C (json/toml/genome/klein4/the_one/carriers/cd) OR a pure accessor / constructor / validator (the 16 genome ops, the 5 carrier *_from_coeffs + 4 ladder promote/project, coupling 6, cascade.one 6 accessors, cd/sedenion/topk 6, text 3, the write_ndjson/validate_mpr/tlv_unpack/klein4_project/classify_harmonic/chirality_parity/beat_relation_residue/write_packed_graph misc 8, the 2 form rings, descriptor load/render 2, qm attestation tables 3, the 3 catalog data-parse rows, polyphase.decompose). These get a TRANSITIVE-REACHABILITY assert, NOT a ceiling.
- host_glue (2) — filesystem / host I/O: descriptor.discover_descriptors (FS scan) + catalog.register_attested_root (catalog-root FS registration). Tracked, no ceiling this rc (annex decision pending; the big mcp/cli/agent host-glue is OUT of the ledger — a later rc).
- dev_tooling (27) — a bare-C host never needs it: the tool_schema register/extension/warmup surface (5), gap_suggester (3), the signal_processing mutable plugin-registry / dispatch-lock / lazy-loader / profiling surface (18: cascade_dispatcher + path_registry + profiling), and carrier_ladder.carrier_ladder_descriptor (1). PINNED exempt allowlist (justified, never owed-C). 20 + 65 + 2 + 27 = 114.
The ratchet machinery (tests/test_rosetta_completeness.py). CEIL_NON_COMPUTE_OWED = 20 + test_non_compute_owed_is_monotone_decreasing (tight: live owed_orchestration count == ceiling; only SHRINKS as each orchestration op earns a C path). test_non_compute_composes_c_is_transitively_reachable reuses the standalone-C reachability walk (relocated to conftest.py as rosetta_reached_ledger_ops / rosetta_live_objects / ROSETTA_NOT_READY) to prove every composes_c row hides NO Python kernel (reaches no python_only_debt / bignum_reference / c_exists_unbound leaf — today all 0, so a forward-guard that LOCKS the property against a future re-route). NON_COMPUTE_DEV_TOOLING_EXEMPT (the pinned 27) + test_non_compute_dev_tooling_is_pinned (live dev_tooling set == the allowlist; a new dev_tooling row must be JUSTIFIED, so control logic cannot escape the owed-C ceiling by being mislabeled). host_glue tracked, no ceiling. Plus test_every_non_compute_row_has_a_valid_kind forces every non_compute op to be sub-classified.
New tests (tests/test_non_compute_ratchet_rc170.py, 6). Pins the split COMPLETE + DISJOINT + TIGHT: non_compute total == 114; the non_compute_kind field only on non_compute rows; every non_compute row has a kind in the four; the four counts {owed_orchestration:20, composes_c:65, host_glue:2, dev_tooling:27} sum to 114; CEIL_NON_COMPUTE_OWED == the live owed count == 20; the dev_tooling allowlist == the ledger's dev_tooling set. numpy-absent (stdlib json + the shared conftest live-op walk). The rosetta _ROOTS are UNCHANGED this rc (the bus/dsl/mcp/cli annex is a later rc — this rc organizes the CURRENT 114 only).
[0.9.0rc169]¶
#765 OPTIMIZATION — SUB-QUADRATIC (LEHMER) srmech_bigint_gcd; the bignum-perf arc closes (mul Karatsuba rc168 + gcd Lehmer rc169). The rc168 measurement named the follow-up: the multiply gap was closed, so the whole big-ℚ op became GCD-BOUND (the plain-Euclid reduce ≈ 870 ms of an 890 ms 65536-bit op). This rc replaces that plain Euclid with LEHMER'S algorithm (Knuth TAOCP Vol 2 §4.5.2 Algorithm L; HAC 14.57): the two leading 30-bit "digits" of x, y drive a single/double-word int64 simulation that builds a 2×2 cofactor matrix [[A,B],[C,D]], applied to the FULL bignums in ONE fused multiply-add pass — batching ~30 bits of Euclid steps and replacing the per-step full-precision Knuth divmod. Same O(n²) class, a tiny constant. srmech_bigint_gcd keeps its signature + result; the gcd VALUE is unique, so it is BYTE-IDENTICAL to Euclid for every input. 1 NEW additive symbol (srmech_bigint_gcd_ws_bound) → ABI stays 3; tools.total stays 403 (carrier-internal, no new public op); python_only_debt / bignum_reference stay 0; numpy absent; no libm; no abs(). 6 SSOT files rc168 → rc169.
The Lehmer engine (c/src/srmech_bigint.c) — malloc-free, recursion-free, caller-arena. 30-bit digits keep every inner product < 2^61 (int64-safe; no __int128, JPL Rule 10). The inner simulation (bi_lehmer_simulate) runs the classic two-quotient test (q from (û+A)/(v̂+C) must equal q′ from (û+B)/(v̂+D)) so each emulated step provably matches the FULL-precision quotient; a divergence leaves a valid (possibly identity) matrix. The matrix-apply is fused (bi_lehmer_combine): the combination A·u + B·v is |pos|·posv − |neg|·negv with the positive term the larger (Knuth's opposite-sign cofactor invariant + the combination ≥ 0), computed as ONE bi_mul_1 + ONE bi_mulsub (the SAME single-limb submul the Knuth divisor already uses) with borrow propagation — no product scratch, so the working set is 4 carriers, the same footprint as lean Euclid. When the leading digit gives no progress (B == 0) or v is ≤ 2 limbs, one full bi_divmod_abs step runs. Rule-2 bounded (a 30-bit digit resolves ≪ 64 inner steps; a 2^40 outer guard). srmech_bigint_gcd_ws_bound(a_n, b_n) sizes the arena that ENGAGES Lehmer; a tighter arena transparently falls back to bi_gcd_euclid — the pre-rc169 body verbatim — so NO existing caller (every carrier's ℚ-reduce scratch) can regress (a carrier sized only for old Euclid still gets a byte-identical answer via the fallback). The Python big-ℚ dispatch (_native._gcd_ws) hands a bound-sized arena so the huge gcd-reduce runs Lehmer (hasattr-guarded; an older lib rides the plain symbol unchanged).
Byte-identity (the shared-infra gate). The gcd is unique, so this is proven by (1) a 202-case ctypes sweep against math.gcd across random balanced + asymmetric + structured (coprime consecutive, one-divides-the-other, both-even, powers of two, u64-small, zeros) + the Fibonacci-adjacent Euclid worst case up to 65536 bits — the Lehmer path AND the forced lean-Euclid fallback BOTH match; (2) the C-host smoke's new limb-for-limb Lehmer-vs-lean-Euclid checks (Fibonacci F(2000)/F(1999), gcd(x,x+1), gcd(x,7x), both-even, 2300/2180 — 86/86 both -O2 and -DNDEBUG); (3) the consumer slices re-run green: Qalg tail char_poly/eigvals/eigvec/jordan/factor/capstone + poly/qmat/tripoly/qpoly carriers (387), the rc167 Q-dispatch + rc168 Karatsuba + cyclic/rational parity (148), rosetta completeness + transitive standalone + scaffolding + JPL audit.
The measured win (WSL2 gcc -O2, attested Class-B — and the honest whole-op verdict). Raw C gcd, Lehmer (bound arena) vs forced lean Euclid, balanced n×n limbs: ~2.2× at 1024 bits, ~3.8× at 4096, ~6.2× at 16384, ~7.0× at 65536; on the Fibonacci worst case (every partial quotient 1 — Lehmer's sweet spot): ~3× at 1400 bits, ~12× at 5.5k bits, ~14× at 20.8k bits. The whole big-ℚ op (bigq_mul_c, the rc167 dispatch): rc169-Lehmer vs rc168-Euclid is 1.9× (1024) → 5.9× (65536) — the gcd-bound bottleneck rc168 named is genuinely relieved. The honest residual: srmech's whole big-ℚ op still trails CPython's Fraction at large sizes (~0.7× at 1024 bits, ~0.17× at 65536 on Python 3.10) — but the remaining gap is NO LONGER the scalar gcd. It is (a) Fraction's cross-gcd-first rational multiply (Knuth 4.5.1: gcds on the half-size operands + a smaller product, vs srmech's multiply-then-reduce on the double-size product) — a composite-level strategy orthogonal to the gcd — and (b) CPython's Lehmer using a 2-word (60-bit) window vs srmech's single 30-bit-digit window. Both are named as the next optimizations; neither is the sub-quadratic-gcd deliverable, which is done.
⚠️ Sparse-tower guardrail (checked, trivially). The gcd is the SCALAR bignum layer, BELOW the carriers. It densifies nothing and touches no sparse encoding: no carrier structure (QMat rows-of-Q, Qalg coords-over-m, Qprime exponent dict, theta-sparse forms, CRT / Laplacian-eigenbasis towers) enters the gcd; the sparse towers are untouched by construction.
New tests (tests/test_fast_gcd_rc169.py, 8). The byte-identity sweep vs math.gcd (random / structured / Fibonacci-worst-case / edge, both the Lehmer path and the lean fallback); the gcd_ws_bound contract (positive, O(m), max-keyed, engaging + sufficient); the rc167 big-ℚ gcd dispatch still byte-identical + counter-proven exercised; a ≥ 1.3×-floor speedup assertion at 2000 limbs (measured ~7×). JPL ratchet green (the new engine helpers are ≤ 60 lines, ≥ 2 asserts, no goto/malloc/recursion/abs); pedantic -Werror -Wpedantic clean in BOTH -O2 asserts-live and -DNDEBUG (lib + C smoke).
[0.9.0rc168]¶
#765 OPTIMIZATION — KARATSUBA srmech_bigint_mul (+ the pre-existing Q.__pow__ negative-base/negative-exponent fix). The rc167 measurement named the follow-up ("a Karatsuba srmech_bigint_mul would close the remaining huge-operand multiply gap") — this rc builds it; the multiply gap vs CPython IS closed (parity ≈ 2048 limbs, a win above — see the measurement below, which also honestly locates the next whole-op bottleneck at the gcd). srmech_bigint_mul keeps its signature and is now Karatsuba above a measured 24-limb crossover, schoolbook below, byte-identical to the old schoolbook for every input; every bignum consumer (the whole Qalg tail — char_poly / eigvals / eigvec / jordan / factor — pi, bigexp, the_one, poly/qmat, the rc167 Q-dispatch) inherits the win through the shared symbol. 2 NEW additive symbols (srmech_bigint_mul_ws + srmech_bigint_mul_ws_bound) → ABI stays 3; tools.total stays 403 (carrier-internal, no new public op); python_only_debt / bignum_reference stay 0; numpy absent; no libm; no abs(). 6 SSOT files rc167 → rc168.
The Karatsuba engine (c/src/srmech_bigint.c) — malloc-free, recursion-free, caller-arena. The split x·y = z2·B^{2h} + z1·B^{h} + z0 with z1 = (x0+x1)(y0+y1) − z0 − z2 (h = ceil(xn/2)) runs as an ITERATIVE explicit frame machine (JPL Rule 1: no recursion — a manual stack of ≤ 40 phase-tracked frames; siblings run sequentially so the live stack never exceeds the split depth, ≤ 29 for any uint32 limb count), with z0/z2 written in place into the product region and only the padded sums + middle term in scratch. Unbalanced operands take a chunk schedule (x split at h, y rides whole) so asymmetric shapes recurse toward balanced blocks. Scratch accounting is exact and asserted: a level whose larger operand is m limbs carves ≤ 2m+8 words (balanced: 4·ceil(m/2)+4 for sx/sy/t; chunk: ≤ m), its largest child is ≤ m/2+2 limbs, so srmech_bigint_mul_ws_bound sums the deepest path (≈ 4m words + the frame stack; O(m) BYTES, no malloc anywhere). Three entries, one engine: (1) srmech_bigint_mul_ws(out, a, b, ws, ws_len) — the full split when ws meets the bound; a smaller/NULL arena degrades gracefully (each frame that does not fit leaf-schoolbooks — the arena tunes SPEED, never the result; ws=NULL IS the schoolbook oracle); (2) srmech_bigint_mul — unchanged signature, routes through a bounded internal 8 KiB arena (full split to ≈ 400 limbs, partial split levels above, honest schoolbook tail — the JPL-clean no-malloc answer to "where does a no-arena entry get scratch"); (3) the Python big-ℚ dispatch (_native._bigq_mul_into) hands a full-bound arena so the rc167 Q-dispatch multiplies get the complete split at ANY size (hasattr-guarded; an older lib rides the plain entry).
Byte-identity (the shared-infra gate). The engine writes the same fully-normalised limb sequence as schoolbook for every input — proven by (1) a 732-product × 4-path sweep (plain / full-arena / NULL-arena / 1 KiB-partial-arena) against the CPython-int oracle across random balanced + asymmetric (m ≠ n) + structured (2^k, all-0xFFFFFFFF worst-carry, ±signs, zero, u64-small … 3000-limb huge) shapes; (2) the C-host smoke's new limb-for-limb arena==schoolbook==plain checks + the (B^n−1)² closed form (66/66 both -O2 and -DNDEBUG); (3) the consumer slices re-run green: Qalg tail char_poly/eigvals/eigvec/jordan/factor/capstone/pi (206 tests), pi Chudnovsky/Archimedes/catalog + the_one (171), the rc167 Q-dispatch suite (24, thresholds unchanged), rosetta completeness + transitive standalone + scaffolding (122).
The measured win (WSL2 gcc -O2, attested Class-B — and the honest whole-op verdict). Raw C multiply, Karatsuba (full arena) vs schoolbook, balanced n×n limbs: break-even ≈ 24 limbs (768 bits), 1.3–1.5× at 48–96 limbs, ~2–2.4× at 256, ~2–3× at 512, ~5× at 1024–8192, ~13× at 16384 limbs (≈ 158k digits; schoolbook also falls out of cache there). The plain srmech_bigint_mul (bounded internal arena): ~2.4× at 256 limbs, ~1.9× at 512, parity (no regression, 0.97–1.09×) above its arena cap. Against CPython's OWN Karatsuba int-multiply — the gap rc167 named — the dispatched multiply (marshal included) reaches parity ≈ 2048 limbs and WINS above it (~1.1× at 4096, ~1.2× at 8192 limbs); without the marshal boundary (every C-internal consumer) the full 5–13× applies. The honest whole-op verdict: the composite big-ℚ ops (bigq_mul_c etc.) re-measured by the rc167 methodology stay cost-parity (0.83–1.05× at 1024–65536 bits) — because the whole op is GCD-BOUND, not multiply-bound (the Euclid gcd-reduce dominates both paths above ~1024 bits; the multiply is ~4ms of a ~890ms 65536-bit op). rc167's "close the multiply gap" follow-up is DONE — the remaining whole-op bottleneck is the gcd, so the named next optimization is a sub-quadratic gcd (Lehmer / binary) on the same substrate. The dispatch thresholds _BIGQ_MIN_BITS = 1024 / _GCD_NATIVE_MIN_BITS = 1024 are UNCHANGED (they gate on the measured parity onset, which has not moved).
⚠️ Sparse-tower guardrail (checked, trivially). Karatsuba changes HOW two scalar bignums multiply — the layer BELOW the carriers. It densifies nothing and touches no sparse encoding: no carrier structure (QMat rows-of-Q, Qalg coords-over-m, Qprime exponent dict, theta-sparse forms) enters the multiply; the sparse towers are untouched by construction.
Q.__pow__ negative-base/negative-exponent fix (pre-existing, NOT an rc167 regression). Q(-3, 4) ** -2 raised ValueError("denominator must be positive"): the negative-exponent branch routed the reciprocal (den, num) with a negative numerator straight into rational_pow_uint's positive-denominator contract. Fixed by normalising the reciprocal's sign onto the numerator (b/a == (−b)/(−a), a Class-K sign pin-slot) so Q(-3,4)**-2 == 16/9, Q(-3,4)**-3 == −64/27 — byte-identical to fractions.Fraction across the full sign × exponent sweep (regression-tested small, huge/dispatch-path, integer-valued-rational exponents, __rpow__, and 0**-1 still raising ZeroDivisionError).
New tests (tests/test_karatsuba_and_qpow_rc168.py, 11). The 4-path byte-identity sweep; the mul_ws_bound contract (0 below crossover, O(m) scaling, smaller-operand keyed); the Q-dispatch multiply still byte-identical + counter-proven exercised; a ≥1.2×-floor speedup assertion at 2000 limbs (measured ~4–5×); and the Q.__pow__ regression battery vs Fraction. JPL ratchet green (the new engine helpers are ≤ 60 lines, ≥ 2 asserts, no goto/malloc/recursion/abs); pedantic -Werror -Wpedantic clean in BOTH -O2 asserts-live and -DNDEBUG (lib + C smoke).
[0.9.0rc167]¶
#765 FOUNDATION — the Python-side exact-ℚ NATIVE DISPATCH: the Q scalar carrier's big-operand arithmetic now runs on srmech's OWN C bignum (srmech_bigint), with CPython int demoted to the below-threshold / no-native fallback — the self-hosting capstone the rc156–rc166 Qalg tail unblocked. Plus #770: the decimal marshal is replaced by a LINEAR, cap-free binary limb marshal (measured 42×→8559× faster round-trip) that every existing srmech_bigint consumer inherits. NO new C symbol (the composites orchestrate the EXISTING srmech_bigint_add/mul/gcd/divmod; binding existing symbols is additive) → ABI stays 3; tools.total stays 403 (the dispatch is carrier-internal, no new public op); python_only_debt / bignum_reference stay 0; numpy stays absent; no libm; no abs(). 6 SSOT files rc166 → rc167.
#770 — the marshal decision: BINARY LIMB MARSHAL (better than both proposed options; zero new C). srmech_bigint's wire format is base-2³² little-endian sign-magnitude over caller-owned limbs — i.e. exactly the magnitude's little-endian bytes zero-padded to a 4-byte limb boundary. Python's int.to_bytes/from_bytes("little") emit/read that form LINEARLY (a binary→binary re-pack of CPython's 2³⁰-digit representation), so _native._bigint_from_int/_bigint_to_int now memmove straight into/out of the limb buffer: no decimal string, no O(digits²) base conversion, no 4300-digit int↔str DoS cap (_ensure_int_str_limit is deleted — nothing converts int↔str on the marshal path anymore; the pure-Python decimal paths in rational.py keep their own chunked 9-digit formatter). The proposed hex-marshal C symbols (srmech_bigint_from_hex/to_hex) are NOT needed — hex would still round-trip through an ASCII string; binary goes straight to the limbs. Measured round-trip (WSL2 gcc -O2, Python 3.12): 42× at 4096 bits, 446× at 16384 bits, 8559× at 110000 bits (~33k digits). Every existing native bigint consumer (the bigexp series, bigint_isqrt_c, the poly/qmat/eigvec/jordan/factor marshalling, the_one, …) inherits the linear boundary. The decimal C symbols stay exported (the C-host surface is unchanged). Byte-identical by construction (same integer values; the no-leading-zero-limb + sign==0 ⇔ n==0 invariants preserved).
#765 — the size-adaptive big-ℚ dispatch (THREE-TIER, policy measured not guessed). Every exact-ℚ scalar op now dispatches by operand size: (1) u64-fit → the existing scalar C ops (srmech_rational_add/mul/div, unchanged); (2) mid-size bignums → CPython int (the complete fallback body, unchanged); (3) at/above _BIGQ_MIN_BITS = 1024 (max operand magnitude bit-length) the WHOLE op — cross-multiplies, signed add, gcd-reduce, the two exact divisions — runs on srmech_bigint via the new _native.bigq_add_c / bigq_mul_c / bigq_div_c / bigq_reduce_c composites (one linear marshal in, one out; intermediates never leave C). Wired into rational.rational_add/rational_mul/rational_div + _reduce_rational, so the Q carrier (q.py) and EVERY carrier whose ℚ components ride Q (QMat / Qalg / Poly / QPoly / TriPoly / EllBase coercions) dispatches transparently. cyclic.gcd's beyond-u64 branch likewise dispatches to srmech_bigint_gcd (_native.bigint_gcd_c) at _GCD_NATIVE_MIN_BITS = 1024. On arena overflow or absent native the composites return None and the pure body proceeds — never an error path. A module counter _native.BIGQ_DISPATCH_COUNT proves the native path is genuinely exercised (the rc167 tests assert it moves — no silent fallback).
The honest measurement (attested Class-B; the thresholds are set FROM it, and it CORRECTS the working assumption). The working assumption was "srmech_bigint wins for HUGE bignums". Measured (WSL2 gcc -O2, Python 3.12, binary marshal): below 1024 bits the native composite clearly LOSES (0.26–0.83×); from 1024 bits up it is cost-parity, not a speedup — 1024: 1.02–1.26×, 2048–65536: 0.59–1.24× (~0.9× average). CPython int is itself a tuned C bignum (Karatsuba multiply above ~2.5k bits vs srmech's schoolbook; the pure Euclid loop's per-iteration a % b is already C divmod, so there is no interpreter-loop win to harvest). 1024 bits is the measured parity onset — the dispatch above it buys the #765 self-hosting architecture (Python's exact-ℚ big arithmetic on the same substrate a bare-C host uses; the numpy-removal discipline applied to CPython int) at ≈cost-neutral. The genuine raw-perf win of this rc is the #770 marshal (which is what makes cost-parity reachable at all). Known follow-up: a Karatsuba srmech_bigint_mul would close the remaining huge-operand multiply gap and turn parity into a win.
⚠️ Sparse-tower guardrail (checked). The dispatch operates on ℚ COMPONENTS only — two scalar bignums per op. No carrier's sparse organization (QMat rows-of-Q, Qalg coords-over-m, Qprime exponent dict, Poly coefficient list, theta-sparse forms) is flattened, densified, or restructured anywhere in this rc; the carriers' code is untouched except the one qprime.similarity line below. Verified in-test: a huge-coord Qalg product preserves m + the coords-tuple shape while its ℚ components dispatch.
The ONE representative carrier repoint (+ the proof of the pattern). The exact-ℚ carriers already carry Q components (their earlier repoint rcs), so the missing repoint was UNDER Q — this rc supplies it, and every Q-carrying carrier inherits the dispatch with zero code change (proven on Qalg field ops + Q itself in the rc167 tests: huge-coord Qalg.__mul__/__truediv__ dispatch — the counter moves — and stay byte-identical to a fractions.Fraction oracle). The one genuinely-Fraction-internal carrier site left — qprime.similarity's Fraction(dot², ‖a‖²·‖b‖²) intermediate — is repointed to construct the Q directly (byte-identical: Q's Class-N reduce IS the same lowest-terms/positive-den canonicalisation), making qprime.py fractions-free.
New tests (tests/test_q_native_dispatch_rc167.py). Q byte-identical to Fraction across + − × ÷ // % ** + comparisons at small AND huge (up to 5000-bit) operands; the binary marshal round-trips 0 / ±1 / ±10⁵⁰⁰⁰ / a 200000-bit value (cap-free — over the old 4300-digit ceiling) and agrees with the decimal bridge; the native path is PROVEN exercised (dispatch counter) for big ops and PROVEN not-taken below threshold; forced-fallback (native gate off) is byte-identical to dispatched results; cyclic.gcd big-operand dispatch parity; the Qalg huge-coord field-op parity + sparse-structure check; the qprime.similarity repoint parity; a perf sanity pinning the dispatch as correct + exercised with the measured cost-parity documented.
[0.9.0rc166]¶
Qalg TAIL Batch 9 — THE CAPSTONE: the last 2 exact-symbolic oracles (eig_exact + jordan_form_exact) earn a standalone-C path, driving CEIL_BIGNUM_REFERENCE 2 → 0 — the ENTIRE exact-algebra tail is now python-free. With rc161–rc165 every COMPUTE dependency of the two turnkey capstones became a srmech_* C twin — char_poly (srmech_faddeev_leverrier), factor_integer_poly (srmech_factor_integer_poly, Zassenhaus), eigvals_exact (srmech_sturm_isolate / srmech_complex_isolate / srmech_poly_root_box_certify), eigvec_exact (srmech_eigvec_exact over the ℚ(λ) field), jordan_chains_exact (srmech_jordan_chains). So eig_exact and jordan_form_exact are now THIN Python orchestrations that ONLY compose already-c_dispatched ops with trivial glue — there is no irreducible compute kernel left in the orchestration — and they move bignum_reference → composition_of_c (the mat_dot / factor-Yun / esprit precedent: a bare-C host orchestrates the C leaves the same way). NO new C symbol; ABI stays 3; tools.total stays 403; numpy stays absent; no libm; no abs(). 6 SSOT files rc165 → rc166.
The 2 capstones leave bignum_reference (BYTE/STRUCTURALLY-IDENTICAL native == forced-pure — the same eigenvalues, the same eigenvectors, the same {P, J}, the same Qalg reps, the same ordering + block structure):
-
amsc.cascade.matrix_cascades.eig_exact→composition_of_c. The turnkey exact eigensolver is a pipeline of already-C ops:char_poly(a)→factor_integer_poly(the irreducible minimal polynomialsmᵢwith algebraic multiplicities) → for eachmᵢ_roots_of_irreducible(isolate ALL roots viaeigvals_exacton the companion — the Sturm real + argument-principle complex C kernels) → each rootλbecomes aQalgovermᵢ(the canonical generatorα, the isolated root carried only for the terminal projection) →eigvec_exact(the exact null space over ℚ(λ)) +jordan_chains_exact(the complete generalized basis). The remaining orchestration is trivial glue: assemble the per-eigenvalue dicts, sort by(re, im), the ONE terminal Qalg→float/complex rotation-last projection, and the float/exact self-validation (Σ mult == n,Π(x−λ) ≈char-poly,A·v ≈ λ·v,A·P ≈ P·J) — a Class-M reduction reaching no non-standalone leaf. -
amsc.cascade.matrix_cascades.jordan_form_exact→composition_of_c. The exact Jordan canonical form chains the SAME C pieces (char_poly→factor_integer_poly→_roots_of_irreducible→ per-rootjordan_chains_exact), then buildsP(the generalized-eigenvector columns, chain by chain) andJ(block-diagonal Jordan —λon the diagonal, a super-diagonal 1 within each chain) by pure reindexing, projects rotation-last, and self-validatesA·P == P·JEXACTLY over Qalg (a Qalg matmul — themat_dot-style reduction) plus the ~1e-9 float read-out.
Value oracles (native == forced-pure, both project=True/False): diag(1,2,3) → eigenvalues {1,2,3} + standard eigenvectors + J = diag; symmetric [[2,1],[1,2]] → {1,3}; defective [[5,1],[0,5]] → a size-2 Jordan block (J = [[5,1],[0,5]], A·P == P·J); [[0,1],[1,1]] → the ℚ(√5) spectrum (min-poly x²−x−1, the golden-ratio Qalg reps (1±√5)/2); a 3×3 defective [[2,1,0],[0,2,0],[0,0,3]] → blocks [(2, size 2), (3, size 1)].
THE MILESTONE — CEIL_BIGNUM_REFERENCE 2 → 0 (the bignum_reference bucket is now EMPTY): the tight monotone ratchet asserts live-count == ceiling == 0 (the empty-bucket guard logic holds — test_bignum_reference_is_monotone_decreasing + test_bignum_reference_rows_are_justified both pass at 0 rows). CEIL_PYTHON_ONLY_DEBT stays 0; CEIL_C_EXISTS_UNBOUND stays 0. With all three non-standalone buckets empty, test_rosetta_transitive_standalone is green by construction (there is no non-ready leaf for any composition_of_c op — including the two new capstones — to reach). New parity test tests/test_qalg_capstone_c_rc166.py. This CLOSES the Qalg exact-algebra tail: a bare C host now does the FULL exact eigendecomposition + Jordan canonical form (char-poly → factor → roots → eigenvectors → Jordan chains → assemble) with no Python. The #765 Python-side Q-carrier native-dispatch capstone is now unblocked.
[0.9.0rc165]¶
Qalg TAIL Batch 8 — the exact IRREDUCIBLE factorization of an integer polynomial over ℚ (factor_integer_poly, Zassenhaus) earns a srmech_bigint-backed C path: CEIL_BIGNUM_REFERENCE 3 → 2. This is the LAST hard build of the exact-algebra tail: factor_integer_poly factors an integer polynomial into its irreducible ℤ[x] factors (Gauss's lemma), the arbitrary-precision oracle eig_exact needs to factor the characteristic polynomial into the minimal polynomials of the eigenvalues. The full classical Zassenhaus stack — 𝔽_p[x] Cantor–Zassenhaus + Hensel lift + subset recombination — is built in C from scratch (there was no 𝔽_p[x] mod-p poly, no Hensel, no Cantor–Zassenhaus in the library before). ABI stays 3 (additive; the ctypes shim hasattr-guards the new symbols); tools.total stays 403 (NO new public op — factor_integer_poly moves buckets); numpy stays absent; no libm; no abs(). 6 SSOT files rc164 → rc165.
The op leaves bignum_reference (BYTE/STRUCTURALLY-IDENTICAL native == forced-pure — the same irreducible factors, the same multiplicities, the same order):
amsc.cascade.matrix_cascades.factor_integer_poly→c_dispatched. The Zassenhaus core —_factor_square_free_primitive, which factors a SQUARE-FREE PRIMITIVE integer polynomial into its irreducibles — dispatches to the NEW C kernelsrmech_factor_squarefree_primitive: (1) choose a primep ∤ leadwith the input square-free mod p; (2) factor mod p in 𝔽_p[x] — distinct-degree (gcd(f, x^(pᵈ) − x)) then Cantor–Zassenhaus equal-degree split (randomr,gcd(r^((pᵈ−1)/2) − 1, g)); (3) quadratic Hensel-lift the mod-p factors to modpᵏ ≥ 2·B+1(Bthe Mignotte coefficient bound); (4) recombine over increasing subset sizes (product modpᵏ, symmetric integer reps, leading-coeff cofactor, exact ℤ trial-division), guarded by a subset-size cap. The mod-p arithmetic is plainu64(p < 100000→ every product< 2³⁴); the Hensel lift + recombination + Mignotte bound are exactsrmech_bigint. The equal-degree split's random polynomials are drawn from a DETERMINISTIC xorshift64 rng that reproduces the Python rng stream byte-for-byte (same seed0x2545F4914F6CDD1D ^ (p·(deg+1)), same algorithm, same draw sequence), so the whole internal computation is identical — and since the ℤ[x] factorization is UNIQUE, the factors + multiplicities + (Python-sorted) order are byte-identical to the pure_factor_square_free_primitive(which stays the Pyodide / no-native fallback + the parity oracle). The content + Yun square-free decomposition (over ℚ) + merge + sort orchestration stays in the shared Python wrapper — its gcds are the already-C-backedsrmech_poly_gcd/srmech_bigint_gcd, so both native and pure paths run it identically. (Superseded by the rc165 completion below: the orchestration is now ALSO a single C call, srmech_factor_integer_poly.) Value oracles:x²−1 → (x−1)(x+1);x²+1irreducible;x⁴−1 → (x−1)(x+1)(x²+1); cyclotomicsΦ₈ = x⁴+1/Φ₁₂ = x⁴−x²+1irreducible;(x²+1)(x²+2);(x−2)(x+3)(x²+x+1);x⁶−1/x⁸−1; repeated-root multiplicities(x−1)²(x+2)/(x−1)³/4(x−1)²(x+1)²(Yun); multiply-backΠ factorᵐᵘˡᵗ == input. A DETERMINISTIC all-pairs product-of-irreducibles sweep (198 cases) + linear triples pass native == forced-pure with 0 failures (the sweep replaced an earlier randomized stress for reproducibility; the completion below documents the honest investigation of the input that motivated the swap).
The new C kernel (c/src/srmech_factor_poly.c; header in c/include/srmech.h). srmech_factor_squarefree_primitive (+ the sizing peers srmech_factor_squarefree_primitive_out_cap / _ws_bound) — 3 additive symbols — plus ~40 static helpers: the 𝔽_p[x] layer (fp_mulmod_s / fp_powmod_s / fp_trim / fp_sub / fp_deriv / fp_mul / fp_make_monic / fp_divmod / fp_gcd / fp_mulreduce / fp_polypow_u64 / fp_polypow_big), the Cantor–Zassenhaus layer (fac_rng_next the xorshift64 rng / fp_distinct_degree / fp_equal_degree / fp_factor_mod_p), the bignum-poly-mod-m layer (bp_addmod / bp_submod / bp_mulmod / bp_divmod_monic / bp_reduce), the Hensel lift (fp_product / fp_bezout / hs_ghstar / hs_ststar / hensel_step / multi_lift_one / multi_lift), the recombination (fac_symmetric_rep / fac_primitive / fac_exact_divmod / next_combo / fac_candidate / fac_peel / fac_recombine), and the Mignotte modulus + prime selection (fac_build_modulus / fac_choose_prime / fac_is_prime / bignum_mod_u64). Caller-arena (every 𝔽_p u64 buffer + the bignum poly pool + the lifted / recombination carriers all carved from ws; a too-small arena / degree > cap / coefficient overflow → SRMECH_ERR_OVERFLOW → the byte-identical pure fallback; the zero polynomial / no-good-prime-below-100000 → SRMECH_ERR_BAD_INPUT), JPL-clean (≥ 2 asserts, ≤ 60 lines, no goto/malloc/libm/abs/recursion — every sign is a Class-K srmech_bigint-sign pin-slot; the recombination subset enumeration is an iterative index-array next_combo, not recursion). Pedantic -Werror -Wpedantic clean in BOTH -O2 asserts-live and -DNDEBUG. Additive symbols → ABI stays 3. The Python wrapper caps the native path at deg ≤ 48 (the arena scales ~deg³; a larger degree routes to the byte-identical pure path — correct, just Python-arena-bounded).
Note — an upstream srmech_bigint_divmod latent bug surfaced during the core build (when q == NULL the throwaway quotient was sized cap = 0, so a NEGATIVE dividend's floor-fixup q -= 1 OVERFLOWed it) and was initially worked around (fbi_mod passed a real caller-owned quotient sink). The rc165 completion (below) fixed the root and removed the workaround.
1 row moved (factor_integer_poly → c_dispatched); CEIL_BIGNUM_REFERENCE 3 → 2 (the tight monotone test asserts live-count == ceiling); CEIL_PYTHON_ONLY_DEBT stays 0; #928 completeness ratchet green; test_rosetta_transitive_standalone green (factor_integer_poly's C path reaches only srmech_poly_* + srmech_bigint C — never eig_exact / jordan_form_exact, which stay bignum_reference). New parity test tests/test_qalg_factor_c_rc165.py. The remaining 2 bignum_reference rows are the exact-symbolic capstones (eig_exact / jordan_form_exact) — the next batch B9 orchestrates char_poly → factor → eigvals → eigvec → jordan_chains (all now C) → CEIL_BIGNUM_REFERENCE 0, the exact-algebra tail fully python-free.
rc165 completion — the three deferrals of the core build, done right (same rc; version stays 0.9.0rc165; ABI stays 3; tools.total stays 403; CEIL_BIGNUM_REFERENCE stays 2):
-
srmech_bigint_divmodNULL-sink bug ROOT-FIXED (shared infra). Theq == NULL/r == NULLconvenience contract ("skip that output") bound acap = 0throwaway, so ANY skipped output needing ≥ 1 limb spuriously returnedSRMECH_ERR_OVERFLOW— everyq == NULLdivision with|a| ≥ |b|, every negative-dividend floor-fixup (q -= 1/r += b), and everyr == NULLmulti-limb (Knuth-path) division. The fix carves a REAL throwaway sink off the front of the caller arenaws(bi_carve_sink:a->n + 2limbs for q;max(a->n, b->n) + 2for r) and hands the tail to the divide + fixup, so the documented NULL contract holds for EVERY input. Thefbi_modworkaround insrmech_factor_poly.cis REMOVED (it calls the fixedsrmech_bigint_divmod(NULL, …)directly; theqsinkcarve is gone). Regression pins inc/test/test_srmech_bigint.c:q==NULLnegative dividend (-7 mod 2 → 1),q==NULLpositive (7 mod 3 → 1),r==NULLfloored quotient (-7 // 2 → -4), and a multi-limb Knuth-pathq==NULLcase (-(10⁴⁰+11) mod 10²⁰ → 10²⁰−11). Divmod consumers re-verified byte-identical: pi (Archimedes cascade + Chudnovsky),char_poly(Faddeev–LeVerrier/k), the Qalg field reduce, eigvals slices (188 tests), the bigint C smoke (55/55 both-O2and-DNDEBUG), and the full factor parity suite. -
The FULL-C
srmech_factor_integer_poly(everything-mirrors). The content + Yun-square-free + merge + sort orchestration left in the Python wrapper by the core build is now a SINGLE C call:srmech_factor_integer_poly(+_out_cap/_ws_bound, 3 additive symbols → ABI stays 3) runs content + primitive part, the Yun square-free decomposition over EXACT ℚ (composingsrmech_poly_gcd/srmech_poly_divmod/srmech_poly_sub+ an exact-ℚ derivative with reduced rational coefficients), per square-free part the Zassenhaus coresrmech_factor_squarefree_primitive, merge-identical factors, the(len, coeffs)sort, and a multiply-back self-check (Π factor^mult == primitive inputexactly; a mismatch returnsSRMECH_ERR_OVERFLOWso the wrapper falls back to the pure oracle — never a silently wrong answer).factor_integer_polydispatches to it FIRST (deg ≤ 32 — the composite arena adds the exact-ℚ poly-gcd chain tail which grows ~cubically with degree); the 32 < deg ≤ 48 band keeps the core-ship behavior (Python orchestration + per-part C core); the pure body stays the byte-identical fallback + oracle everywhere. A bare-C host factors an integer polynomial with ONE call (check_fullcases inc/test/test_srmech_factor_poly.c:x⁴−1, content+multiplicity3(x−1)²(x+2), Yun pair4(x−1)²(x+1)², mixed(x²+1)³(x−5),x⁸−1— both-O2and-DNDEBUG). Byte-identical to pure across the value oracles + the 198-case all-pairs sweep + linear triples;test_full_composite_is_a_single_c_callpins thatfactor_integer_poly_citself (not the orchestration) produced the answer. -
The "pathologically slow degree-10 input" investigated honestly (deferral 3). An extensive reproduction attempt (≈2 400 cases: random products of irreducible blocks at coefficient magnitudes 9 → 10¹⁸, the full all-pairs/triples of the committed
_BLOCKS, many-linear / dense / multiplicity / surd shapes) found NO degree-10 input slower than ~17 ms on either path — the removed stress input was never committed and is unrecoverable, and nothing in the pipeline is pathological at degree 10 (≤ 2¹⁰ candidate subsets). The GENUINE wall is the classic Zassenhaus subset recombination — WORST-CASE EXPONENTIAL in the number of modular factors — measured on the textbook Swinnerton-Dyer family: SD4 (deg 16 → 8 quadratics mod p) = 259 candidates ≈ 40 ms; SD5 (deg 32 → 16 quadratics mod p) = 65 539 candidates ≈ 24 s (both paths). Verdict: (a) + (b). (a) FIXED a genuine classical inefficiency: the enumeration tested subset sizes up to#remainingwhere the classical algorithm stops at2·size ≤ #remaining(von zur Gathen & Gerhard, Modern Computer Algebra, ch. 15 — a factor spanning more than half the modular factors has an already-peeled smaller cofactor); applied IDENTICALLY to the pure Python and the C core (results unchanged — the leftover is appended as the final irreducible; byte-identity preserved). Measured: SD5 65 539 → 39 207 candidates; pure 24.2 s → 13.1 s; native 24.1 s → 4.7 s (the full-composite C path). (b) The remaining exponential is the FUNDAMENTAL Zassenhaus recombination wall; the known real fix is van Hoeij's LLL knapsack recombination (M. van Hoeij, "Factoring polynomials and the knapsack problem", J. Number Theory 95(2), 167–189, 2002) — documented in the code + UPSTREAM_NOTES (Note 3) and deferred as a research arc. Bounded representatives RESTORED to the test (not hidden): SD4 with parity on both paths; SD5 on the dispatch path with the measured numbers + the honest CI-budget note.
[0.9.0rc164]¶
Qalg TAIL Batch 7b — the exact JORDAN CHAINS (jordan_chains_exact, the generalized eigenvectors of a DEFECTIVE eigenvalue) earn a srmech_bigint-backed C path via the rc163 Qalg number-field carrier: CEIL_BIGNUM_REFERENCE 4 → 3. Where rc163's eigvec_exact returns the GEOMETRIC eigenvectors (the null space of N = A − λI), a defective eigenvalue has FEWER of those than its algebraic multiplicity μ — so this batch closes the gap with the generalized eigenvectors / Jordan chains. N is nilpotent on the generalized eigenspace null(Nᵘ) (dim μ); the Jordan structure is read off the exact Qalg-RREF ranks of the matrix POWERS Nᵏ (# blocks of size exactly k = r_{k-1} − 2·r_k + r_{k+1}) and the chains are built TOP-DOWN. ABI stays 3 (additive; the ctypes shim hasattr-guards the new symbols); tools.total stays 403 (NO new public op — jordan_chains_exact moves buckets); numpy stays absent; no libm; no abs(). 6 SSOT files rc163 → rc164.
The op leaves bignum_reference (BYTE/STRUCTURALLY-IDENTICAL native == forced-pure — the same chains: the same Qalg generalized eigenvectors, the same chain lengths, the same ordering):
amsc.cascade.matrix_cascades.jordan_chains_exact→c_dispatched. The exact Jordan chains of an integer/rational matrix for aQalgeigenvalue λ. Dispatches to the NEW C kernelsrmech_jordan_chains: buildN = A − λIwithQalgentries over ℚ(λ); compute the ranks ofN, N², …, Nᵏ(a new Qalg matrix MATMUL + RANK) until the nullity stabilises; read the block-size counts off the rank drops; then TOP-DOWN, for block sizesfrompdown to 1, pick a generalized eigenvectorvinnull(N^s)INDEPENDENT (over ℚ(λ)) ofnull(N^{s-1})∪ the chains already chosen (a nested NULLSPACE + column-rank check) and form the chainv, N·v, …, N^{s-1}·v(stored bottom→top, the bottom a genuine geometric eigenvector). The RREF is CANONICAL (unique) + the selection deterministic, so byte/structurally-identical to the pure_jordan_chains_build_pure(which stays the Pyodide / no-native fallback + the parity oracle, AND the arbiter of the reducible-m / n-above-native-cap semantics). Value oracles: a diagonalizable matrix → all chains length 1 (= the eigenvectors); a Jordan block[[λ,1],[0,λ]]→ one chain of length 2; the generalized-eigenvector defining relationsN·vₖ == v_{k-1}andN·bottom == 0hold EXACTLY overQalg; a mixed defective matrix → the correct per-eigenvalue chain structure; an IRRATIONAL eigenvalue (ℚ(√5)) and a bignum Jordan block → byte-identical chains.
The new C ops COMPOSE the rc163 Qalg field carrier — the added surface is the Qalg matrix layer: a MATMUL (for Nᵏ), a general (rectangular) RREF → RANK + nested NULLSPACE, a column-rank independence check, and the chain-building MATVEC, all over the same qalg_field_mul / qalg_field_sub / qalg_field_inverse exact-ℚ(λ) arithmetic (which itself composes the exact-Q srmech_poly_* kernels). A bare-C host now computes the full Jordan-chain decomposition of a defective matrix with no Python.
The new C symbols (c/src/srmech_qalg.c; header in c/include/srmech.h). srmech_jordan_chains (+ the sizing peers srmech_jordan_chains_entry_cap / _ws_bound) — 3 additive symbols — plus the static Qalg-matrix helpers (qalg_gmatmul / qalg_gmatvec / qalg_grref / qalg_geliminate / qalg_gnullspace / qalg_rank_of / qalg_nullspace_of / qalg_col_rank / qalg_cand_independent / qalg_build_chain / qalg_jordan_powers / qalg_block_counts / qalg_topdown_s / qalg_jordan_carve / qalg_jordan_prepare / qalg_field_add / qalg_set_identity / qalg_fill_col / qalg_build_context) reusing the rc163 field arithmetic + arena carve. Caller-arena (N + the stored n+2 matrix powers + the RREF / column-rank / null-basis scratch + the srmech_poly_* tail all carved from ws; a too-small arena / coordinate cap → SRMECH_ERR_OVERFLOW → the byte-identical pure fallback; a REDUCIBLE m → SRMECH_ERR_BAD_INPUT → the pure path raises the same ValueError), JPL-clean (≥ 2 asserts, ≤ 60 lines, no goto/malloc/libm/abs/recursion). Pedantic -Werror -Wpedantic clean in BOTH -O2 asserts-live and -DNDEBUG. Additive symbols → ABI stays 3. The Python wrapper caps the native path at n ≤ 24 (the stored-powers arena is ~(n+2)·n²·deg field cells; a larger n routes to the byte-identical pure path — correct, just Python-arena-bounded).
1 row moved (jordan_chains_exact → c_dispatched); CEIL_BIGNUM_REFERENCE 4 → 3 (the tight monotone test asserts live-count == ceiling); CEIL_PYTHON_ONLY_DEBT stays 0; #928 completeness ratchet green; test_rosetta_transitive_standalone green (jordan_chains_exact's C path reaches only srmech_poly_* + srmech_bigint C via the Qalg carrier — never eig_exact / jordan_form_exact / factor_integer_poly, which stay bignum_reference). New parity test tests/test_qalg_jordan_c_rc164.py. The remaining 3 bignum_reference rows are the exact-symbolic capstones (eig_exact / factor_integer_poly / jordan_form_exact) — the next batches (B8 Zassenhaus factor_integer_poly, B9 eig_exact + jordan_form_exact).
[0.9.0rc163]¶
Qalg TAIL Batch 7a — the exact EIGENVECTORS over the number field ℚ(λ)=ℚ[x]/(m) (eigvec_exact + eigvec_exact_float) earn a srmech_bigint-backed C path: CEIL_BIGNUM_REFERENCE 6 → 4. This is the SECOND hard Qalg foundation: where the eigenVALUE isolation (rc162) stays in plain ℚ, the eigenVECTORS carry ALGEBRAIC-NUMBER coordinates — for an eigenvalue λ that is a root of an IRREDUCIBLE monic integer polynomial m of degree d, ℚ(λ)=ℚ[x]/(m) is a FIELD and the eigenvector lives in the null space of A − λI over that field. So this batch ships a genuine Qalg number-field carrier + Gaussian elimination over it in C (srmech_qmat_* is ℚ-only, insufficient here). ABI stays 3 (additive; the ctypes shim hasattr-guards the new symbols); tools.total stays 403 (NO new public op — eigvec_exact / eigvec_exact_float move buckets); numpy stays absent; no libm; no abs(). 6 SSOT files rc162 → rc163.
The ops leave bignum_reference (BYTE/STRUCTURALLY-IDENTICAL native == forced-pure — the same null-space basis, the same ℚ(λ) coordinates, the same ordering + normalization):
amsc.cascade.matrix_cascades.eigvec_exact→c_dispatched. The exact eigenvector(s) = the null space ofA − λIover ℚ(λ), for aQalgeigenvalue λ. Dispatches to the NEW C kernelsrmech_eigvec_exact: buildM = A − λIwithQalgentries (the diagonal subtracts λ) and run EXACT Gaussian elimination over the ℚ(λ) FIELD (pivot on the first nonzeroQalgat/below the current row → normalize by the pivot'sQalgINVERSE → clear every other row) to reduced row echelon form; each free column gives one null-space basis vector (v[fc]=1, pivot vars =−M[pivot row][fc]). The RREF is CANONICAL (unique), so byte/structurally-identical to the pure_eigvec_exact_qalg(which stays the Pyodide / no-native fallback + the parity oracle, AND the arbiter of the reducible-m / non-eigenvalue error semantics). Value oracles:diag(1,2,3)→ the standard-basis eigenvectors; symmetric[[2,1],[1,2]]→(1,1)/(1,−1)for λ=3 / 1;A·v == λ·vholds EXACTLY overQalgfor every (eigenvector, eigenvalue) pair; a matrix with an IRRATIONAL eigenvalue ([[1,1],[1,2]], ℚ(√5)) → the eigenvector with the correct ℚ(λ) entries (−2 + 1·α); a repeated eigenvalue (2·I₃) → the fulllist[list[Qalg]]basis.amsc.cascade.matrix_cascades.eigvec_exact_float→c_dispatched. The terminal float/complex read-out ofeigvec_exact— the ONE rotation-last projection (Qalg.to_complex/.to_floatper component) over the same exact-Qalgbody; it rides the same native path (exactQalgresult, then the per-component FPU lift).
The Qalg field arithmetic COMPOSES the exact-Q srmech_poly_* kernels (no re-derived ℚ machinery): add/sub coefficientwise (srmech_poly_add / _sub); mul = polynomial convolution (srmech_poly_mul) then REDUCE mod m (srmech_poly_divmod remainder — the monic relation αⁿ = −Σ m[i]αⁱ done as exact long division); inverse = the extended Euclidean algorithm on b(x), m(x) in ℚ[x] (u·b + v·m = g, g a nonzero constant since m is irreducible + b ≠ 0, so b⁻¹ = u/g reduced mod m) — cofactor-tracked over srmech_poly_divmod / _mul / _sub.
The new C symbols (c/src/srmech_qalg.c; header in c/include/srmech.h). srmech_eigvec_exact (+ the sizing peers srmech_eigvec_exact_entry_cap / _ws_bound) — 3 additive symbols — plus the static Qalg-carrier + RREF helpers (qalg_field_mul / qalg_field_sub / qalg_field_inverse / qalg_euclid_step, the arena carve qalg_carve / qalg_eng_carve, the qalg_build_matrix / qalg_rref / qalg_eliminate / qalg_extract pipeline). Caller-arena (the n·n·deg Qalg matrix + every working carrier + the srmech_poly_* scratch tail carved from ws; a too-small arena / coordinate cap → SRMECH_ERR_OVERFLOW → the byte-identical pure fallback; a REDUCIBLE m → SRMECH_ERR_BAD_INPUT → the pure path raises the same ValueError), JPL-clean (≥ 2 asserts, ≤ 60 lines, no goto/malloc/libm/abs/recursion — every sign is a Class-K numerator sign-flip on the srmech_bigint sign field; the RREF uses a row-permutation index, no data move). Pedantic -Werror -Wpedantic clean in BOTH -O2 asserts-live and -DNDEBUG. Additive symbols → ABI stays 3.
2 rows moved (eigvec_exact + eigvec_exact_float → c_dispatched); CEIL_BIGNUM_REFERENCE 6 → 4 (the tight monotone test asserts live-count == ceiling); CEIL_PYTHON_ONLY_DEBT stays 0; #928 completeness ratchet green; test_rosetta_transitive_standalone green (eigvec's C path reaches only srmech_poly_* + srmech_bigint C via the Qalg carrier — never another bignum_reference oracle; it does NOT compose eig_exact / jordan). New parity test tests/test_qalg_eigvec_c_rc163.py. The remaining 4 bignum_reference rows are the exact-symbolic eigen/Jordan/factor capstones (eig_exact / factor_integer_poly / jordan_chains_exact / jordan_form_exact) — the next batches (B7b jordan_chains_exact via the Qalg carrier, B8 Zassenhaus factor_integer_poly, B9 eig_exact + jordan_form_exact).
[0.9.0rc162]¶
Qalg TAIL Batch 6 — the exact eigenvalue ISOLATION (eigvals_exact, the ROOTS of the char-poly) earns a srmech_bigint-backed C path, BOTH the real AND the complex spectrum: CEIL_BIGNUM_REFERENCE 7 → 6. The eigenvalues of an integer matrix are ALGEBRAIC numbers, not transcendental, so — kept in exact integer/rational arithmetic the whole way — they come out as exact isolating rational intervals (real) / certified rational boxes (complex) with NO Wilkinson ill-conditioning. This batch closes eigvals_exact FULLY (real + complex) on top of rc161's char_poly. ABI stays 3 (additive; the ctypes shim hasattr-guards the new symbols); tools.total stays 403 (NO new public op — eigvals_exact moves buckets); numpy stays absent; no libm; no abs(). 6 SSOT files rc161 → rc162.
The op leaves bignum_reference (BYTE/STRUCTURALLY-IDENTICAL native == forced-pure — the same isolating intervals, the same box centers, the same ordering + multiplicities):
amsc.cascade.matrix_cascades.eigvals_exact→c_dispatched. The exact real (default) + complex (include_complex=True) eigenvalues. The REAL path dispatches to the NEW C kernelsrmech_sturm_isolate:char_poly(rc161'ssrmech_faddeev_leverrier) → Yun square-free factorisation (exact multiplicities) → STURM sign-sequence isolation (the Sturm chain viasrmech_poly_divmod; the sign-variation count viasrmech_poly_eval) → rational BISECTION to width< 2^-bits— returning the exact isolating(lo, hi)Fraction intervals with multiplicity. The COMPLEX path dispatches to the NEW C kernelsrmech_complex_isolate: pure rational-box subdivision over the upper half-plane, each box CERTIFIED bysrmech_poly_root_box_certify— the exact argument principle (the winding number = the Cauchy-index sum of the per-edgeV/Ugeneralised-Sturm sign-variation sequences, in exact Fraction arithmetic, NO float in the count) — refined to2^-bits, emitting each certified upper-half center + its conjugate with multiplicity. Both compose the exact-Qsrmech_poly_*kernels (gcd / divmod / eval / add / sub / mul) with scalar exact-Qsrmech_bigintarithmetic, byte/structurally-identical to the pure_square_free_factors+_isolate_real_roots+_isolate_complex_roots_upper(which stay the Pyodide / no-native fallback + the parity oracle). The caller SORTS bylo+hi/(re, im)and projects to float (the single terminal rotation) identically on both paths. Value oracles:diag(1,2,3) → {1,2,3};[[2,1],[1,2]] → {1,3};2·I₃ → {2,2,2}(multiplicity 3); a companion matrix → the roots of its polynomial;[[0,-1],[1,0]] → {±i};x³−1companion →{1, −½ ± (√3/2)i}; the isolating intervals BRACKET the exact eigenvalues (a rational sanity check, no float).
The new C symbols (c/src/srmech_sturm.c; header in c/include/srmech.h). srmech_sturm_isolate (+ srmech_sturm_isolate_entry_cap / _ws_bound), srmech_poly_root_box_certify (+ _ws_bound), srmech_complex_isolate (+ srmech_complex_isolate_entry_cap / _ws_bound) — 8 additive symbols — plus their static helpers (the exact-Q scalar st_rat_*, the srmech_poly_* glue, the Sturm chain / sign-variation / bisection, the edge-substitution U+iV accumulation, the generalised-Sturm Cauchy index, the box subdivision / root-free split / refinement). Caller-arena (all working polys / scalars / the interval + box stacks carved from ws; a too-small arena / entry cap / a subdivision beyond the bounded stack → SRMECH_ERR_OVERFLOW → the byte-identical pure fallback), JPL-clean (≥ 2 asserts, ≤ 60 lines, no goto/malloc/libm/abs/recursion — every sign is a Class-K numerator sign-flip on the srmech_bigint sign field). Pedantic -Werror -Wpedantic clean in BOTH -O2 asserts-live and -DNDEBUG. Additive symbols → ABI stays 3.
1 row moved (eigvals_exact → c_dispatched); CEIL_BIGNUM_REFERENCE 7 → 6 (the tight monotone test asserts live-count == ceiling); CEIL_PYTHON_ONLY_DEBT stays 0; #928 completeness ratchet green; test_rosetta_transitive_standalone green (eigvals_exact's C path reaches only char_poly + srmech_poly_* + srmech_bigint C — never another bignum_reference oracle). New parity test tests/test_qalg_eigvals_c_rc162.py. The remaining 6 bignum_reference rows are the exact-symbolic eigenvector / Jordan / integer-poly-factor tail (eigvec ×2 / eig_exact / factor_integer_poly / jordan ×2) — the next batches (B7 the Qalg-field Jordan LA, B8 Zassenhaus factor_integer_poly, B9 the eig_exact capstone).
[0.9.0rc161]¶
Qalg TAIL Batch 5 — the exact-INTEGER characteristic polynomial (char_poly, Faddeev–LeVerrier) earns a srmech_bigint-backed C path: CEIL_BIGNUM_REFERENCE 8 → 7. char_poly is the FOUNDATION of the exact-LA tail — eigvals_exact (roots of the char-poly), eig_exact, and the Jordan ops all reduce to it — so it ships first, standalone-C, for the later batches to compose. An integer matrix has integer char-poly coefficients, so there is NO ℚ carrier here (contrast rc160's srmech_cd_mult): the kernel is pure srmech_bigint integer arithmetic. ABI stays 3 (additive; the ctypes shim hasattr-guards the new symbol); tools.total stays 403 (NO new public op — char_poly moves buckets); numpy stays absent; no libm; no abs(). 6 SSOT files rc160 → rc161.
The op leaves bignum_reference (exact-integer → BYTE-IDENTICAL native == forced-pure coefficient list):
amsc.cascade.matrix_cascades.char_poly→c_dispatched. The exact-integer characteristic polynomialdet(xI − A)of an integer matrix. Dispatches to the NEW C kernelsrmech_faddeev_leverrier, which runs the exact-integer Faddeev–LeVerrier recursion —M_1 = I; forkin1..n:AM = A·M,c_k = -tr(AM)/k,M ← AM + c_k·I— composingsrmech_bigintmul/add (theA·Mmatmul + trace accumulate) with the exactsrmech_bigintdivmod (the/kstep; the/kis EXACT becausek | tr(A·M_k)by the FL integer theorem, so divmod's FLOOR quotient IS the exact quotient and the remainder is zero — asserted in checked builds). Byte-identical to the pure_char_poly_int(the SAME recursion), which stays the Pyodide / no-native fallback and the parity oracle. The working matricesM/AMgrow to the determinant-HadamardB^nenvelope, soqmat_cap_for(cl, 2n+4)sizes every carrier from the input limb count (a too-small arena →SRMECH_ERR_OVERFLOW→ the pure fallback). Value oracles:diag(1,2,3) → [1,-6,11,-6]=(x-1)(x-2)(x-3); a companion matrix recovers its defining polynomial;I_n → (x-1)^n(alternating binomials); the Cayley–Hamilton identityp(A) = 0.
The new C symbols (c/src/srmech_qmat.c; header in c/include/srmech.h). srmech_faddeev_leverrier (+ the sizing peers srmech_faddeev_leverrier_entry_cap / _ws_bound + the static helpers fl_max_limbs / fl_carve / fl_matmul / fl_trace_ck / fl_next_m), caller-arena, JPL-clean (≥ 2 asserts, ≤ 60 lines, no goto/malloc/libm/abs/recursion — the Class-K sign of c_k = -(tr//k) is a numerator sign-flip). Reuses the qmat arena carve (qmat_take / qmat_bind / qmat_hdr_words) + the qmat_cap_for magnitude envelope IN-TU (no duplicated machinery). Pedantic -Werror -Wpedantic clean in BOTH -O2 asserts-live and -DNDEBUG. Additive symbol → ABI stays 3.
1 row moved (char_poly → c_dispatched); CEIL_BIGNUM_REFERENCE 8 → 7 (the tight monotone test asserts live-count == ceiling); CEIL_PYTHON_ONLY_DEBT stays 0; #928 completeness ratchet green; test_rosetta_transitive_standalone green (char_poly's C path reaches only srmech_bigint + qmat C — never another bignum_reference oracle). New parity test tests/test_qalg_charpoly_c_rc161.py. The remaining 7 bignum_reference rows are the exact-symbolic eigen/Jordan/factor tail (eigvals_exact / eigvec ×2 / eig_exact / factor_integer_poly / jordan ×2) — the next batches (B6 eigvals_exact via srmech_sturm_isolate → 6, …).
[0.9.0rc160]¶
Qalg TAIL Batch 4 — the Cayley–Dickson MULTIPLICATION core earns a C path: CEIL_BIGNUM_REFERENCE 13 → 8. rc159 gave the CD ℚ-VECTOR (basis/conjugate/add/norm_sq) a srmech_cd_qvec carrier; this batch adds the arbitrary-rational PRODUCT and its dependents, so a bare-C host now MULTIPLIES Cayley–Dickson elements and computes the hypercomplex exp with no Python. ABI stays 3 (additive; the ctypes shim hasattr-guards the new symbol); tools.total stays 403 (NO new public op — existing ops moving buckets); numpy stays absent; no libm; no abs(). 6 SSOT files rc159 → rc160.
The 5 ops leave bignum_reference (exact-ℚ → BYTE-IDENTICAL native == forced-pure reduced (num, den)):
amsc.cascade.cayley_dickson.cd_mult→c_dispatched. The exact-rational CD productx·y. Dispatches to the NEW C kernelsrmech_cd_mult, which computes the bilinear form(x·y)_{i⊕j} = Σ_{i,j} x_i·y_j·sign(i,j)by composing the integer cocyclesrmech_cd_basis_productwith the SAME qmat exact-ℚ arithmetic (qmat_q_mul/qmat_q_add/qmat_q_reduce) the rc159 Qvec kernels use — the ℚ algebra is NOT duplicated (it lives next to the Qvec kernels insrmech_qmat.c, sharingsrmech_cd_qvec_ws_bound/_entry_capsizing; each output slot sums exactlydimproducts, the same profile ascd_norm_sq). Bilinearity makes the cocycle-sum the SAME rational as the recursive_multdoubling, both reduced to canonical form → byte-identical at any sedenion+ magnitude. Value oracles: quaternion unitsi·j = k([0,1,0,0]·[0,0,1,0] = [0,0,0,1]); the octonion non-associativity witness(e1·e2)·e4 ≠ e1·(e2·e4).amsc.cascade.cayley_dickson.left_mult_matrix→composition_of_c. Then×nmatrixL(x)ofu ↦ x·u; each columnx·e_cis acd_mult(C) over acd_basis(C) unit vector — a Python loop over the C multiplication (themat_dotprecedent). Value oracle:L(unit)is a signed permutation.amsc.cascade.cayley_dickson.left_mult_kernel→composition_of_c. The exact-ℚ kernel ofL(x); dispatches to the existingsrmech_qmat_nullspaceoverleft_mult_matrix(byte-identical fallback = the pure_rational_nullspace— both the SAME classical free-variable basis). Value oracles: a sedenion zero divisor has a NON-empty kernel; an invertible element's kernel is empty.qm.octonion.octonion_exp_series_truncate/qm.quaternion.quaternion_exp_series_truncate→composition_of_c. The exact-rational hypercomplexexp(e_axis·θ)= Euler formula on a single unit axis; each packs the already-c_dispatchedrational.cos_series_truncate/sin_series_truncate(thesrmech_{cos,sin}_series_truncate_bigexact-ℚ Taylor peers) into the 4-/8-tuple. They were only PARKED inbignum_reference— the compute already reached C — so this is the honest reclassification (no new kernel). Value oracle: matches the exact Taylor of a pure-imaginary rotation.
The new C symbol (c/src/srmech_qmat.c; header in c/include/srmech.h). srmech_cd_mult (+ the static term-accumulator cd_mult_accum), caller-arena, JPL-clean (≥ 2 asserts, ≤ 60 lines, no goto/malloc/libm/abs/recursion — the Class-K sign of a bilinear term is a numerator sign-flip). Pedantic -Werror -Wpedantic clean in BOTH -O2 asserts-live and -DNDEBUG. Additive symbol → ABI stays 3.
5 rows moved (cd_mult → c_dispatched; left_mult_matrix / left_mult_kernel / octonion_exp_series_truncate / quaternion_exp_series_truncate → composition_of_c); CEIL_BIGNUM_REFERENCE 13 → 8 (the tight monotone test asserts live-count == ceiling); CEIL_PYTHON_ONLY_DEBT stays 0; #928 completeness ratchet green; test_rosetta_transitive_standalone green (the moved ops reach only srmech_cd_basis_product + qmat + srmech_bigint C — never another bignum_reference oracle). New parity test tests/test_qalg_cdmult_c_rc160.py. The remaining 8 bignum_reference rows are the exact-symbolic LA + integer-poly-factor tail (char_poly / eigvals / eigvec ×2 / eig / factor_integer_poly / jordan ×2) — the next batches (B5 char_poly → 7, B6 eigvals_exact → 6, …).
[0.9.0rc159]¶
Qalg TAIL Batch 3 — the 4 TRIVIAL Cayley–Dickson EXACT-ℚ arithmetic ops earn a C path via a new srmech_cd_qvec exact-ℚ VECTOR carrier: CEIL_BIGNUM_REFERENCE 17 → 13. This is the FIRST exact-ℚ-carrier Qalg batch (rc156–158 cleared series/π on srmech_bigint + the integer-cocycle CD navigation). A Cayley–Dickson element of dim 2^k is a ℚ-vector of dim components, each one exact rational num/den srmech_bigint pair (reduced, den > 0, gcd 1 — the SAME canonical form as Python's fractions.Fraction). The new srmech_cd_qvec is the 1-D sibling of srmech_qmat; a bare-C host now CONSTRUCTS + HOLDS + MANIPULATES a CD ℚ-vector with no Python (the everything-mirrors discipline — the carrier is owed C, not only the primitive kernels). ABI stays 3 (additive; the ctypes shim hasattr-guards each new symbol); tools.total stays 403 (NO new public op — existing ops moving buckets); numpy stays absent; no libm; no abs(). 6 SSOT files rc158 → rc159.
The 4 ops (bignum_reference → c_dispatched; exact-ℚ → BYTE-IDENTICAL native == forced-pure reduced (num, den)):
amsc.cascade.cayley_dickson.cd_basis→c_dispatched. The unit basis vectore_i(1/1 ati, 0/1 elsewhere). Dispatches tosrmech_cd_qbasis(Class-A basis convention; no arithmetic). Value oracle:cd_basis(4,2)=[0, 0, 1, 0].amsc.cascade.cayley_dickson.cd_conjugate→c_dispatched. Negate the IMAGINARY half (components 1..dim−1), keep component 0 — the Class-K sign-flip (numerator sign, never an ALUabs(); reduced form preserved). Dispatches tosrmech_cd_qconjugate. Value oracle:cd_conjugate([1,2,3,4])=[1, −2, −3, −4].amsc.cascade.cayley_dickson.cd_add→c_dispatched. Component-wise exact-ℚ sum. Dispatches tosrmech_cd_qadd. Value oracle:cd_add([1/2,1/3],[1/6,1/3])=[2/3, 2/3].amsc.cascade.cayley_dickson.cd_norm_sq→c_dispatched. The squared normN(x) = Σ x_i²as one exact-ℚ scalar (x·x̄ = N(x)·1; Class-N rational anchor). Dispatches tosrmech_cd_qnorm_sq. Value oracles:cd_norm_sq([1,2,2,0])=9;cd_norm_sq([1/2,1/2])=1/2;cd_norm_sq([3/4,5/6])=181/144.
The new carrier + 6 C symbols (c/src/srmech_qmat.c; header in c/include/srmech.h). srmech_cd_qbasis / srmech_cd_qconjugate (no arena — bignum copy/set + the Class-K sign flip) + srmech_cd_qadd / srmech_cd_qnorm_sq (caller-arena) + the sizing pair srmech_cd_qvec_ws_bound / srmech_cd_qvec_entry_cap. They REUSE the qmat exact-ℚ scalar machinery (qmat_q_add / qmat_q_mul / qmat_q_reduce over the qmat_ctx_t roster + qmat_cap_for sizing) in the SAME translation unit — the rational-limb arithmetic is not duplicated (the 1:1-mirror discipline forbids two copies of one algebra; the 1-D vector is a degenerate matrix). Byte-identical to Python's Fraction (num, den) at ANY magnitude (full bignum; no int64/Q61 ceiling; a genuinely huge input → SRMECH_ERR_OVERFLOW → the ceiling-free pure-Fraction oracle). JPL-clean (every function ≥ 2 asserts, ≤ 60 lines, no goto/malloc/libm/abs/recursion); pedantic -Werror -Wpedantic clean in BOTH -O2 asserts-live and -DNDEBUG. Additive symbols → ABI stays 3.
4 rows moved (cd_basis/cd_conjugate/cd_add/cd_norm_sq → c_dispatched); CEIL_BIGNUM_REFERENCE 17 → 13 (the tight monotone test asserts live-count == ceiling); CEIL_PYTHON_ONLY_DEBT stays 0; #928 completeness ratchet green; test_rosetta_transitive_standalone green (the 4 ops LEAVE the not-ready bignum_reference bucket). New parity test tests/test_qalg_qvec_c_rc159.py (byte-identical native == forced-pure across the exact-ℚ vector surface + the value oracles above + the ledger rows). Next: B4 = srmech_cd_mult (the recursive CD doubling product over ℚ) + left_mult_matrix/kernel (via srmech_qmat_nullspace) + octonion/quaternion_exp → CEIL 13 → 8.
[0.9.0rc158]¶
Qalg TAIL Batch 2 — the 4 Cayley–Dickson INTEGER-cocycle NAVIGATION ops earn a C path: CEIL_BIGNUM_REFERENCE 21 → 17. FOUR new C kernels give the signed-basis-unit loop navigation a standalone-C surface, COMPOSED over the existing srmech_cd_basis_product cocycle (they do NOT re-implement the multiplication table). These are genuinely INTEGER (signed units ±e_i, bounded ≤ 2·dim ≤ 128; NO bignum, NO ℚ, NO new carrier) — a bare-C host navigates the whole Cayley–Dickson Moufang loop with no Python. ABI stays 3 (additive; the ctypes shim hasattr-guards each new symbol); tools.total stays 403 (NO new public op — existing ops moving buckets); numpy stays absent; no libm; no abs(). 6 SSOT files rc157 → rc158.
The 4 ops (bignum_reference → c_dispatched; INTEGER → BYTE-IDENTICAL native == forced-pure, incl. set/list ordering):
amsc.cascade.cayley_dickson.closure→c_dispatched. The sub-loop fixpoint: seed{(+1,e0)}+ each(+1,e_g), close under all pairwise signed-unit products until no new unit appears. Dispatches to the newsrmech_cd_closure. Returns asetof(sign, index)— the C set is element-identical to the pure fixpoint (order-independent). Value oracles:closure(8,[1,2])= the 8-unit quaternion sub-loop;closure(16, 1..15)= the full 32-unit sedenion loop.amsc.cascade.cayley_dickson.left_orbit→c_dispatched. One left-multiplication cycle[e_s, e_g·e_s, …]in cycle order. Dispatches tosrmech_cd_left_orbit. Byte-identical walk order to the pure list. Value oracle:left_orbit(8,1,1)=[(1,1),(-1,0),(-1,1),(1,0)](order 4).amsc.cascade.cayley_dickson.min_generating_set→c_dispatched. The smallestkwhosek-subsetclosurespans the full loop. Dispatches tosrmech_cd_min_generating_set, which COMPOSESsrmech_cd_closureover each combination (a bounded k-combination odometer + the closure cardinality check; the search cap →SRMECH_ERR_OVERFLOW→ the complete pure oracle). Returnsk>0, or0= no spanning subset (→ the sameValueErroras pure). Value oracles: octonions= 3, quaternions= 2, ℂ= 1, sedenions= 4.amsc.cascade.cayley_dickson.sedenion_zero_divisor_witness→c_dispatched. The first sedenion basis-pair zero divisor, searched in the SAME nested order (i<j,k<l,s∈{+1,-1}) as the Python oracle. Dispatches tosrmech_cd_zero_divisor_witness; the Python rebuilds the identical dict from the returned(i,j,k,l,s). Value oracle:(e1 + e10)(e4 − e15) = 0(both factors norm 2, product all-zero).
The 4 new C kernels (c/src/srmech_cayley_dickson.c). srmech_cd_closure / srmech_cd_left_orbit / srmech_cd_min_generating_set / srmech_cd_zero_divisor_witness, over the shared static helpers cd_loop_mult / cd_closure_impl / cd_next_combination / cd_subset_spans / cd_dedup_units / cd_pair_product_is_zero — all composing srmech_cd_basis_product. Fixed-size caller/stack arenas (a 2·dim ≤ 128 presence bitset + element arrays; no malloc); explicit JPL Rule 2 loop bounds (2·dim+1 orbit over-bound; the SRMECH_CD_MGS_MAX_UNITS/SRMECH_CD_MGS_MAX_SUBSETS search caps → SRMECH_ERR_OVERFLOW). JPL-clean (every function ≥ 2 asserts, ≤ 60 lines, no goto/malloc/libm/abs/recursion); pedantic -Werror -Wpedantic clean in BOTH -O2 asserts-live and -DNDEBUG. Additive symbols → ABI stays 3.
4 rows moved (closure/left_orbit/min_generating_set/sedenion_zero_divisor_witness → c_dispatched); CEIL_BIGNUM_REFERENCE 21 → 17 (the tight monotone test asserts live-count == ceiling); CEIL_PYTHON_ONLY_DEBT stays 0; #928 completeness ratchet green; test_rosetta_transitive_standalone green (the 4 ops LEAVE the not-ready bignum_reference bucket). New parity test tests/test_qalg_cdnav_c_rc158.py (byte-identical native == forced-pure across the loop-navigation surface + the value oracles above + the ledger rows). Next: B3 Qvec (bignum-ℚ vector carrier) + the 4 trivial CD ops (cd_basis/cd_conjugate/cd_add/cd_norm_sq) → CEIL 17 → 13.
[0.9.0rc157]¶
Qalg TAIL Batch 1b — the π family earns a srmech_bigint-backed C path: CEIL_BIGNUM_REFERENCE 24 → 21. ONE new C symbol srmech_pi_archimedes runs the WHOLE Pfaff–Archimedes two-mean chiral-pair loop in C, so amsc.rational.pi_cascade_digits DISPATCHES to it and its 2 signal-processing wrappers free-ride. ABI stays 3 (additive; the ctypes shim hasattr-guards the new symbol); tools.total stays 403 (NO new public op — these are existing ops moving buckets); numpy stays absent; no libm; no abs() (Class-K sign is the srmech_bigint sign-magnitude carrier). 6 SSOT files rc156 → rc157.
The 3 ops (bignum_reference → c_dispatched / composition_of_c; all EXACT → BYTE-IDENTICAL native == forced-pure):
amsc.rational.pi_cascade_digits→c_dispatched. The Archimedes bracket —b₀ = 3·M,a₀ = isqrt(12·M²)over the fixed-point unitM = 1 << precision_bits, then per step the harmonic meana' = (2·a·b)//(a+b)(asrmech_bigint_divmod) and the geometric meanb' = isqrt(a'·b)(asrmech_bigint_isqrt) — now runs its ENTIRE loop in the new caller-arena C kernelsrmech_pi_archimedes(composingsrmech_bigintmul/shl/add/divmod/isqrt/to_dec). Because the whole loop lives in C, the marshal is MINIMAL — ONE finalfloor(π·10ⁿ)bigint out, NOT the O(digits²) per-step decimal round-trip the rc156 wiring still paid (rc156 routed only the per-stepisqrtto C; the LOOP stayed Python). The kernel early-exits at the exacta == bfixed point (a' = (2·a²)//(2a) = a,b' = isqrt(a²) = a— a pure speedup, not a result change, since the pair converges quadratically). Byte-identical to the pure-Python fixed-point oracle acrossn ∈ {1, 10, 50, 100, 500, 1000}; the STRONGEST cross-checkpi_chudnovsky_digits(n) == pi_cascade_digits(n)(two independent π algorithms — rotation-last vs projects-every-step — agree) holds. The pure-Python body remains the COMPLETE fallback (no-C / Pyodide) + the parity oracle.signal_processing.closed_form_ops.pi_cascade.op/signal_processing.path_b_ops.pi_cascade.op→composition_of_c. Both are thin wrappers overrational.pi_cascade_digits; once the coupler isc_dispatchedthey are pure compositions of a C-backed op.
The new C kernel srmech_pi_archimedes (c/src/srmech_pi_archimedes.c). The COMPLEMENT of srmech_pi_chudnovsky (rotation-last): where Chudnovsky keeps a bit-exact body and rotates ONCE, the Archimedes chiral pair projects at EVERY step (one integer isqrt per iteration = the geometric mean). All 9 working bigints + the divmod/isqrt scratch are bump-carved from the CALLER arena (no malloc; per-carrier cap = 2·M_limbs + D_limbs + 32 covers the 12·M² and pi_scaled·10ⁿ intermediates; too-small → SRMECH_ERR_OVERFLOW → the complete pure fallback, never a wrong answer). JPL-clean (arch_take/arch_bind/arch_ctx_init/arch_init_bounds/arch_step/arch_converge/arch_make_tenpow/arch_project/arch_render + the public entry each ≥ 2 asserts, ≤ 60 lines, no goto/malloc/libm/abs, bounded loops); pedantic -Werror -Wpedantic clean in BOTH -O2 asserts-live and -DNDEBUG. Additive symbol → ABI stays 3.
3 rows moved (pi_cascade_digits → c_dispatched; the 2 sp wrappers → composition_of_c); CEIL_BIGNUM_REFERENCE 24 → 21 (the tight monotone test asserts live-count == ceiling); CEIL_PYTHON_ONLY_DEBT stays 0; #928 completeness ratchet green; test_rosetta_transitive_standalone green (the 3 ops LEAVE the not-ready bignum_reference bucket). New parity test tests/test_qalg_pi_c_rc157.py (byte-identical native == forced-pure across n ∈ {1,10,50,100,500,1000} + pi_chudnovsky == pi_cascade agreement + the value oracle str(pi_cascade_digits(1000))[:11] == "3.141592653" + the 2 sp wrappers native == pure). Next: B2 CD integer-cocycle navigation (closure/left_orbit/min_generating_set/zero_divisor_witness over srmech_cd_basis_product, no bignum → CEIL 21 → 17).
[0.9.0rc156]¶
Qalg TAIL Batch 1a — 6 exact-ℚ bignum oracles earn a srmech_bigint-backed C path: CEIL_BIGNUM_REFERENCE 30 → 24. The compute surface is python-free (python_only_debt = 0); the next axis drives CEIL_BIGNUM_REFERENCE (the Python-bignum exact-rational ORACLES without a srmech_bigint-backed C twin) toward 0. This is the cleanest foundation: NO new C kernel — the exact bignum kernels already shipped (rc35-era) and back srmech_the_one; this batch is the Python wiring + one ctypes binding. ABI stays 3 (additive); tools.total stays 403 (NO new public op — these are existing ops moving buckets); numpy stays absent; no libm; no abs() (Class-K). 6 SSOT files rc155 → rc156.
The 6 ops (all bignum_reference → c_dispatched, all EXACT → BYTE-IDENTICAL native == forced-pure):
amsc.rational.{exp,sin,cos,log1p,atan}_series_truncate→c_dispatched. Each exact-ℚ Taylor partial sum now DISPATCHES to the caller-arenasrmech_bigintC peersrmech_{exp,sin,cos,log1p,atan}_series_truncate_big(composingsrmech_bigintmul/pow_u32/gcd/divmod/add/sub— the series LOGIC is genuinely C-reachable, a bare-C host computes the exact(num, den)with no Python bignum). The C peer returns the SAME rational reduced to lowest terms with positive denominator, byte-identical at ANY magnitude (no int64/Q61 ceiling; the int64 fast-path stays as a small-N accelerator forexp). The pure-Python bignum body remains the COMPLETE fallback + the parity oracle. Domain guards (log1p: −1 < p/q ≤ 1;atan: |p/q| ≤ 1; per-opnum_termscaps 512/50/50/64/64) match the C peer exactly. These already existed as thesrmech_the_onefoundation (rc138) — now wired to their own Python ops.qm.bell.tsirelson_bound(2√2) →c_dispatched.TSIRELSON_BOUND = 2·√2viarational.sqrt(2, precision_bits=64), whose precision path calls_integer_sqrt(2¹²⁹)— forn ≥ 2¹²⁸this previously fell to Python_py_isqrt. Now_integer_sqrt's bignum branch dispatches to the caller-arenasrmech_bigint_isqrt(integer-Newton floor-sqrt).floor(√n)is the UNIQUE non-negativerwithr² ≤ n < (r+1)², so the C peer is byte-identical to_py_isqrtand the exact Tsirelson(num, den)matches the pre-change Python exactly. (The same wire also routes the π-cascade radicand's bignum sqrt to C — a strict standalone gain.)
NO new C symbol (the 5 _big series kernels + srmech_bigint_isqrt already exist in srmech_bigexp.c / srmech_bigint.c); the only new Python surface is the srmech_bigint_isqrt ctypes binding + the has_native_bigint_isqrt / bigint_isqrt_c marshal helpers (decimal bridge, like _bigexp_call). 6 rows moved bignum_reference → c_dispatched; CEIL_BIGNUM_REFERENCE 30 → 24 (the #764 tight monotone test asserts live-count == ceiling); CEIL_PYTHON_ONLY_DEBT stays 0; #928 completeness ratchet green; test_rosetta_transitive_standalone green (the 6 ops LEAVE the not-ready bignum_reference bucket → strictly fewer non-standalone leaves). New parity test tests/test_qalg_series_c_rc156.py (byte-identical native == forced-pure across a range of (num, den, num_terms) per op + value oracles: exp(1,1,20)≈e, atan(1,1,·)→π/4, cos(0)=1, log1p(0)=0, tsirelson=2√2). Next: B1b (pi_archimedes + the 2 sp pi_cascade free-riders → CEIL 24 → 21), then B2 CD-navigation (→ 17).
[0.9.0rc155]¶
BATCH B-residue — THE COMPUTE PYTHON-FREE MILESTONE: the FINAL 5 compute ops close python_only_debt 5 → 0. After a 24-rc parity backfill the entire public COMPUTE surface now dispatches to a bit-exact C twin OR is a pure composition of such twins — libsrmech runs standalone (full OS or a thread-less microcontroller) with no host Python for every compute op. ONE new C symbol srmech_jade_jointdiag (+ its _ws_bound); ABI stays 3 (additive); tools.total stays 403 (NO new public op); numpy stays absent; no libm; no abs() (Class-K). 6 SSOT files rc154 → rc155.
The 5 ops (honest per-op classification):
amsc.cascade.spectral_cascades.kron→composition_of_c(BYTE-IDENTICAL for integer / Gaussian-integer). The Kronecker productA⊗Bis the outer productvec(A)·vec(B)ᵀ(a rank-1 matmul — one single-term complex multiply per entry, order-independent) through thec_dispatchedlaplacian.mat_matmul(srmech_dense_matmul_complex), followed by a pure integer block RE-INDEXout[i·mb+k][j·nb+l] = outer[i·na+j][k·nb+l]. VALUE oracle: knownA⊗Btables byte-exact.amsc.cascade.matrix_cascades.einsum→composition_of_c(WITHIN-TOL). A clean 2-operand contraction (matmulij,jk→ik/ matvec / dot / outer / rank-nijk,kl→ijl) routes its Class-M sum-of-products bundle through thec_dispatchedmat_matmul(_einsum_pair_via_matmulgathers A→(∏free_A, ∏contracted), B→(∏contracted, ∏free_B), matmuls, re-indexes to the output order — all glue but the one matmul). Single-operand specs (traceii→/ transposeij→ji) fall back to the general index-iteration, whose multiply-accumulate is primitive glue reaching no non-standalone leaf (themat_dotreduction precedent). VALUE oracle: vs the explicit-sum reference.signal_processing.closed_form_ops.beamforming_fixed.op→composition_of_c(WITHIN-TOL). The delay-and-sum outputout[i] = Σ_m w[m]·sig[m][delay[m]+i]IS the matrix-vector productout = D·wover the delay-aligned window matrixD[i][m] = sig[m][delay[m]+i](the per-mic time-shift is exact integer indexing glue), routed through thec_dispatchedlaplacian.mat_matvec ∘ mat_matmul. VALUE oracle: vs the manual delay-sum.signal_processing.closed_form_ops.jpeg.op→composition_of_c(WITHIN-TOL). The only float KERNEL is the block DCT-II / inverse DCT-III, which runs entirely through the already-composition_of_cdct.op(rc148: the cosine-basis matvec ridesmat_matmul; the sibling Huffman entropy coder isc_dispatched—srmech_huffman_build_codes— for callers that add it); the Wallace quality scaling, the Class-Kround(coeff/qt)quantise, the zigzag/block integer indexing and the dequantise multiply are exact integer/float glue. The rc144 deferral was ONLY because the DCT was not yet C-backed; rc148 closed that. VALUE oracle: encode→decode round-trip ≈ input within quantisation error (MSE ≈ 0.06 at quality 90).signal_processing.closed_form_ops.ica_jade.op→c_dispatched(WITHIN-TOL — THE last real compute gap). ICA-JADE whitening (PCA eig) already composes the Cmat_hermitian_eigendecompose, and the fourth-order cumulant assembly is a plain Class-M accumulate, but the JADE Givens JOINT-DIAGONALISATION of the cumulant slices is a genuinely-ITERATIVE, data-dependent kernel (like the one-sided-Jacobi SVD) — so it earns its OWN standalone-C symbolsrmech_jade_jointdiagrather than a false composition tag. The C peer runs the Givens sweep (theta = 0.25·atan2(2·C[i][j][i][j], C[i][i][i][i]−C[j][j][j][j]), the first-axis tensor rotation twice per step — mirroring the reference exactly), composing the libm-free Class-Nsrmech_atan2/srmech_cos/srmech_sin; all scratch (G + V·G accumulator + rotated-cumulant ping-pong) is bump-carved from the CALLER arena (srmech_jade_jointdiag_ws_bound=(2·k² + k⁴)doubles, no malloc); explicitmax_itersweep cap; noabs()/libm (Class-K sign-branch). JADE's rotation basis is permutation/sign/scale-ambiguous, so native == pure agree WITHIN-TOL on the recovered separation (~5e-11 element-wise onS/W), NOT byte-for-byte. The pure-Python sweep (_jade_sweep_pure) is the COMPLETE alternative + parity oracle.
Why exactly one new C symbol (honest classification). Four ops (kron / einsum / beamforming_fixed / jpeg) decompose into the already-C-backed mat_matmul / mat_matvec / dct foundations + exact integer/float glue → composition_of_c (zero new symbols). Only the JADE Givens joint-diagonalisation is a genuine iterative kernel with no existing C twin, so it gets the minimal srmech_jade_jointdiag. ABI-additive (ctypes shim hasattr-guards it) → ABI stays 3; JPL-clean (jade_rotate_first_axis / jade_matmul / jade_build_givens / jade_pair / jade_sweep / the two public entries each ≥ 2 asserts, ≤ 60 lines, no goto/malloc/libm/abs; bounded max_iter). 5 rows moved; CEIL_PYTHON_ONLY_DEBT 5 → 0 (the compute python-free milestone); CEIL_BIGNUM_REFERENCE = 30 UNCHANGED (B-residue touches no bignum oracle); #928 ratchet green; test_rosetta_transitive_standalone stays green (the 4 composition_of_c ops reach only c_dispatched / composition_of_c leaves). New parity test tests/test_residue_c_rc155.py (VALUE oracles + native == pure per op).
[0.9.0rc154]¶
BATCH B10 — misc, the near-final compute batch: 8 misc ops move python_only_debt → composition_of_c (×7) / c_dispatched (×1). ONE new C symbol srmech_polar_random; ABI stays 3 (additive); tools.total stays 403; NO new public op. The 8 ops: amsc.coupling.signed_sum_squared, amsc.harmonics.classify_chirality_harmonic, amsc.hdc.polar_from_real, amsc.hdc.polar_unbind, amsc.hdc.polar_similarity, amsc.hdc.polar_random, amsc.laplacian.three_fold_eigvec_groups, amsc.compose.greedy_bipartite_alignment. Each was a MIS-BUCKETED composition, not an irreducible kernel (the honest reclassification). numpy stays absent; no libm; no abs() (Class-K). 6 SSOT files rc153 → rc154.
How each routes to C (honest per-op classification):
coupling.signed_sum_squared→composition_of_c(EXACT byte-identical). Pure Class-K bipolar sign-projection (2·bit − 1) ∘ Class-L signed-magnitude-square over integer bit-stacks — no libm, noabs(), reaching no non-standalone leaf → trivially C-portable, the SAME status as thecascade.compose.signed_sum_squaredtwin and themat_dotpure-reduction. VALUE oracle:[[1,1,1]×3] → [9,9,9]; balanced →[0,…];[[0]×4] → [16](the square, noabs()).harmonics.classify_chirality_harmonic→composition_of_c(EXACT — discrete ½/3 label). Pure Class-L inner products (⟨x,x⟩, ⟨x,rev(x)⟩, ⟨x,roll(x)⟩) ∘ Class-K magnitude pin-slots (explicitx if x≥0 else -x, noabs()/nosqrt(·²)) ∘ Class-N ratios; libm-free, reaching no non-standalone leaf. VALUE oracle: DC-dominant → harmonic 1; mirror-symmetric → 2; 3-periodic → 3.hdc.polar_from_real→composition_of_c(byte-identical). The whole real→polar encode IS thec_dispatchedsign_quantise.opcascade (srmech_sign_quantise, rc143) — a C-backed Class-K threshold projection; the int8 collection is trivial integer glue.hdc.polar_unbind→composition_of_c(byte-identical int8). Unbind == bind on the ±1 sub-alphabet (c[i]·a[i]), so it now COMPOSES thec_dispatchedpolar_bind→ routes byte-identically tosrmech_polar_bindwith the pure-Python product as the complete fallback.hdc.polar_similarity→composition_of_c(EXACTQ). A Class-K skip-zero pin-slot ∘ integer match-count → exactQ(matches, denom)(stay-rational, F868). Thesrmech_polar_similarityC peer supplies the display-collapse float for a C host; the test cross-checksfloat(Q) == srmech_polar_similarity. The exact-Qcounting stays Python (reconstructingQfrom the C float would lose exactness).laplacian.three_fold_eigvec_groups→composition_of_c(eig-INVARIANT). Composes thecomposition_of_csymmetric_eigendecompose(C Hermitian-eig) + thec_dispatchedsrmech_three_fold_bands; the column-slice is exact integer glue. Parity is eig-INVARIANT (non-unique Jacobi basis): native == pure agree on the band SIZES (exact) + per-band SPAN, NOT element-wise — the rc146 so7 / rc152casimir_eigenvalueinvariant precedent.compose.greedy_bipartite_alignment→composition_of_c. Greedy argmax + used-set is a pure Class-K selection (s > best_spin-slot) over a caller-suppliedsimilarity_fn— the SAME status astop_k_by_score(Class-E ∘ Class-K). VALUE oracle: a known-similarity table pair → the known one-to-one alignment.hdc.polar_random→c_dispatched(byte-identical seeded). A RANDOM op reaching Python-onlyrandom.Randomhas NO standalone-C path, so it earns its OWN deterministic C RNGsrmech_polar_random(MT19937 seeded byinit_by_arrayover the seed's little-endian words, then_randbelow(3)=getrandbits(2)with rejection of≥ 3,−1 + …→{−1,0,+1}) — BYTE-IDENTICAL torandom.Random(seed).randrange(-1, 2); the polar sibling of the §60srmech_klein4_random(a DIFFERENT stream:_randbelow(3)vs_randbelow(4)). NOT a false composition-of-c. The deterministic integer-seedpath dispatches; a caller-suppliedrng/ the urandomseed=Nonepath stays pure-Python (the complete alternative).
Why one new C symbol (honest classification). Seven ops decompose into already-C-backed primitives / are pure standalone-trivial integer/float compositions → composition_of_c (zero new symbols). Only polar_random is a genuine RNG kernel with no existing C twin — a random op that reaches a Python-only RNG fails the transitive-standalone contract, so it gets a minimal MT19937 C peer rather than a false composition. srmech_polar_random is ABI-additive (ctypes shim hasattr-guards it) → ABI stays 3; JPL-clean (below3 + polar_random each ≥ 2 asserts, ≤ 60 lines, no goto/malloc/libm/abs). 8 rows moved; CEIL_PYTHON_ONLY_DEBT 13 → 5; CEIL_BIGNUM_REFERENCE = 30 UNCHANGED (B10 touches no bignum oracle); #928 ratchet green; test_rosetta_transitive_standalone stays green (the 7 composition_of_c ops reach only c_dispatched / composition_of_c leaves). New parity test tests/test_amsc_misc_c_rc154.py (VALUE oracles + native == pure per op).
Remaining compute python_only_debt = 5: the honestly-hard residue — einsum / kron (tensor contractions), ica_jade (JADE Givens joint-diagonalisation), jpeg (float-DCT), beamforming_fixed (delay-and-sum). Next per roadmap: that residue → python_only_debt = 0 (compute python-free).
[0.9.0rc153]¶
BATCH B7 — modulation: the 3 NUMERIC signal-processing modulation ops move python_only_debt → composition_of_c (tools.total stays 403; ABI stays 3; NO new C symbol). The 3 ops: signal_processing.closed_form_ops.fsk.op (frequency-shift keying), signal_processing.closed_form_ops.ofdm.op (OFDM), signal_processing.closed_form_ops.psk_qam.op (PSK / QAM constellation). Each COMPOSES an EXISTING C foundation — the SAME numeric-DSP contract as the B4 sp_transform batches. numpy stays absent; no libm; no abs(). 6 SSOT files rc152 → rc153.
NUMERIC classification (WITHIN-TOL native == pure, reldiff ≤ 1e-9, differential — NOT byte-identical — the F1-FFT / F2-SVD / B4 / B9 numeric-foundation contract). These are float DSP ops, so the parity test is differential — an FFT butterfly / complex matmul accumulation can FMA-fuse ~1 ULP on some platforms; cross-platform CI is the arbiter. How each routes to C (zero new symbols):
fsk→composition_of_c. Modulate is the C-backed Class-Nrational.cos/rational.sintone cascade (via_exp_i, native libm-free). Demodulate's correlator-bank inner productcorr_k = Σ_j tones[k][j]·conj(window[j])IS the matveccorr = Tones · conj(window)(theM×ntone matrix times the length-nconjugated window) — routed through thec_dispatchedlaplacian.mat_matvec∘mat_matmul(srmech_dense_matmul_complex), the SAME correlator-through-the-C-matmul pattern as the B4bmatched_filter; the Class-C conjugate is a carrier transform (noabs()) and theargmax|corr_k|²is a Class-K decision (monotone in|corr|, nosqrt). VALUE oracle: a pure tone atf_kcorrelates maximally with tonek→ demod recovers the symbol; modulate → demodulate round-trip is the identity.ofdm→composition_of_c. Modulate's IFFT and demodulate's FFT funnel throughspectral_cascades.ifft/.fft→ thec_dispatchednumeric FFT foundationsrmech_fft_c128(rc139) — the SAME pattern as the B4astft/ B4cwienerops. The per-subcarrier|H_k|equaliser rides thecomposition_of_crational.hypot(Class-K pin-slot guard, noabs()); the cyclic-prefix and one-tap divide are numpy-free elementwise / integer glue. VALUE oracle: modulate → demodulate round-trip ≈ identity (no channel).psk_qam→composition_of_c. The constellation build IS the genuine float math — the PSK phasese^{i·2π·k/M}ride thec_dispatchedrational.cos/rational.sinand the QAM grid rides thec_dispatchedrational.sqrt(√M). Modulate is then an integer index lookup; demodulate is a nearest-neighbour Class-K decision-region search over the numpy-free|received − const|²squared-distance glue (monotone in|·|, nosqrt/ noabs(); strict<keeps the first-minimum index). VALUE oracle: mapping symbols → constellation → nearest-neighbour recovers the symbols exactly.
Why no new C symbol (honest classification). Every substantive kernel already has a C twin: the dense matmul (srmech_dense_matmul_complex, backing the FSK correlator matvec), the numeric FFT (srmech_fft_c128, backing OFDM), and the byte-exact Class-N rational.{cos,sin,sqrt} integer-cascade C ports (backing the FSK tones + PSK/QAM constellation). The residual per-op work is numpy-free elementwise / integer / decision glue a bare-C host writes inline — so composition_of_c (×3) is the honest classification (zero new C symbols). Only fsk needed a source change (the demod correlator now routes through mat_matvec); ofdm and psk_qam already composed their C foundations. 3 rows python_only_debt → composition_of_c; CEIL_PYTHON_ONLY_DEBT 16 → 13; #928 ratchet green; test_rosetta_transitive_standalone stays green (no new composition→bignum edge — the _PI precompute is a module-level float constant, not a callee of op). ABI stays 3. New parity test tests/test_sp_modulation_c_rc153.py (within-tol native == pure for the 3 ops + the VALUE oracles above).
PART B — the down-only CEIL_BIGNUM_REFERENCE ratchet (user-directed 2026-07-06: "prevent import of python bignum like we prevent numpy import — it's why we do big int without depends"). srmech ships its OWN srmech_bigint in C (no Python-bignum dependency), so the bignum_reference bucket (the Python-bignum exact-rational ORACLES without a srmech_bigint-backed C twin) is now a DOWN-ONLY tracked debt, mirroring the CEIL_NUMPY_CARRIER / CEIL_PYTHON_ONLY_DEBT ratchets in tests/test_rosetta_completeness.py. Added CEIL_BIGNUM_REFERENCE = 30 (the current live count) + test_bignum_reference_is_monotone_decreasing (tight, down-only: the live bignum_reference row count must == 30 — it may only SHRINK as each oracle earns a C path, NEVER grow). This is the OP-level ledger ratchet, NOT a from fractions import Fraction import ban — the exact-ℚ CARRIERS (Poly / QMat / QPoly / Qalg / Qprime / TriPoly / EllBase) legitimately use Fraction as their Python-side rep and already have srmech_bigint C peers; the guard is on the count of exact-rational ORACLE ops with no C twin, driving it toward 0. The 30 is UNCHANGED this rc (B7 is NUMERIC — none of the 3 modulation ops were bignum_reference); the new ceiling just LOCKS it.
Remaining compute python_only_debt = 13: B10 misc (8) + the honestly-hard residue (ica_jade / einsum / kron / jpeg / beamforming_fixed, ~5). Next per roadmap: B10, then the hard residue → python_only_debt = 0.
[0.9.0rc152]¶
BATCH B9 — qm-numeric: the 9 NUMERIC qm ops (norms / eigenvalue-invariants / time-evolution that produce floats) move python_only_debt → composition_of_c (tools.total stays 403; ABI stays 3; NO new C symbol). The 9 ops: bell.chsh_operator_norm, bell.chsh_pauli_combination_norm, bell.verify_chsh, gauge.casimir_eigenvalue, pseudo_hermitian.construct_eta_from_eigendecomposition, pseudo_hermitian.is_pseudo_hermitian, pseudo_hermitian.pseudo_hermitian_eigenvalues_real, single_particle.heisenberg_evolve, single_particle.liouville_evolve. Each is a pure composition of the already-C-backed matrix algebra — the c_dispatched laplacian.mat_matmul (srmech_dense_matmul_complex) + mat_hermitian_eigendecompose (srmech_hermitian_eigendecompose_ws) + mat_solve (srmech_dense_solve_f64_ws) + the composition_of_c mat_norm / mat_eigvals + the byte-exact Class-N rational.{sqrt,cos,sin,cexp} integer-cascade C ports. numpy stays absent; no libm; no abs(). 6 SSOT files rc151 → rc152.
Two honest parity sub-classes (per the rc147 B8c / rc148+ B4 numeric precedent — byte-identity is claimed ONLY where there is genuinely no float reduction to FMA-fuse).
- 0 byte-exact (all 9 within-tol; chsh_pauli_combination_norm rerouted to the C Hermitian eig — eigvals_exact has no C twin) —
chsh_pauli_combination_norm. The primary CHSH identity‖σ_x⊗σ_x + σ_z⊗σ_z‖ = 2is computed through the EXACT-INTEGER eigenvalue cascadematrix_cascades.eigvals_exact(char-poly Faddeev-LeVerrier → Sturm isolation → rational bisection — allFraction/int, thebignum_referenceoracle, itself standalone-ready) over the byte-exact integerMatadd, so the value is exactly2.0and native == pure is byte-identical AND platform-invariant. - 8 FLOAT / eig-INVARIANT (WITHIN-TOL native == pure, reldiff ≤ 1e-9, differential — NOT byte-identical).
chsh_operator_norm(‖B_CHSH‖ = 2√2Tsirelson via the Jacobimax|λ|),verify_chsh(bool verdict + residuals: primary exact-0.0, Tsirelson<1e-14),casimir_eigenvalue(trace(T^aT^a)/dim = (N²−1)/(2N)= ¾ SU(2), 4/3 SU(3) — the byte-exactcasimir_operatormatmul + a pure-Python trace/divide),construct_eta_from_eigendecomposition,is_pseudo_hermitian(bool + residual),pseudo_hermitian_eigenvalues_real(bool +max|Im λ|),heisenberg_evolve(A(t)=U†AU),liouville_evolve(ρ(t)=UρU†). These bottom out in the non-unique Jacobi eigenBASIS and/or a multi-term complexmat_matmulaccumulation that can FMA-fuse ~1 ULP cross-platform (the rc147 ckm lesson), so byte-identity is NOT claimed; the physics INVARIANT (η Hermitian + pseudo-Hermiticity;A(0)=A+ Hermiticity;ρ(0)=ρ+ trace preservation) + the scalar/bool VALUE are asserted native == pure within-tol.
One source change (construct_eta_from_eigendecomposition). Its eigenvector null-space null(O − λI) formerly used a hand-rolled float Gaussian-elimination RREF — a Python-only kernel with NO C twin (it would block honest composition_of_c). It now routes through the C-backed Gram Hermitian-eigendecomposition: G = Mᴴ M (mat_matmul, c_dispatched) → mat_hermitian_eigendecompose(G) (c_dispatched, ascending) → the smallest-eigenvalue eigenvector is the null direction — the SAME SVD/Gram-eig null-space pattern the rc146 so(8) subalgebra builders (so8._svd_nullspace) use, per [[feedback_cascade_svd_nullspace_accuracy_not_route_matrix_rank]]. All 14 existing pseudo_hermitian tests still pass (η stays Hermitian + positive; O stays η-pseudo-Hermitian). The other 8 ops needed NO source change — they already purely compose C-backed carriers.
Why no new C symbol (honest classification). Every substantive kernel already has a C twin (srmech_dense_matmul_complex / srmech_hermitian_eigendecompose_ws / srmech_dense_solve_f64_ws + the byte-exact rational.* ports + the exact-integer eigvals_exact bignum oracle). The residual per-op work is numpy-free scalar / index glue a bare-C host writes inline — so composition_of_c (×9) is the honest classification (zero new C symbols). 9 rows python_only_debt → composition_of_c; CEIL_PYTHON_ONLY_DEBT 25 → 16; #928 ratchet green. ABI stays 3. New parity test tests/test_qm_numeric_c_rc152.py (within-tol / invariant native == pure for the 9 ops + the VALUE oracles: 2√2 Tsirelson, exact 2, Casimir ¾ & 4/3, verify_chsh True, is_pseudo_hermitian True, A(0)=A, ρ(0)=ρ). Remaining compute python_only_debt = 16: B7 (fsk / ofdm / psk_qam modulation, 3) + B10 misc (8) + the honestly-hard residue (ica_jade / einsum / kron / jpeg / beamforming_fixed, ~5). Next per roadmap: B7 + B10, then the hard residue.
[0.9.0rc151]¶
BATCH B4d — sp_transform part 4, CLOSES B4: interpolators / wavelet / spectral-subtraction — the last 4 NUMERIC DSP ops move python_only_debt → composition_of_c (tools.total stays 403; ABI stays 3; NO new C symbol). B4 = sp_transform (numeric DSP ops that USE the F1 FFT / the dense matmul); B4d = the interpolator + wavelet + spectral-subtraction family (4 ops): closed_form_ops.farrow, closed_form_ops.sinc_interp, closed_form_ops.wavelet, closed_form_ops.spectral_subtraction. numpy stays absent; no libm; no abs(). 6 SSOT files rc150 → rc151.
NUMERIC classification (WITHIN-TOL native == pure, NOT byte-identical — the F1-FFT / F2-SVD / B4a / B4b / B4c numeric-foundation contract). These are float DSP ops, so the parity test is differential (native == pure to reldiff ≤ 1e-9), NOT byte-equality — an FFT butterfly / convolution matmul can FMA-fuse ~1 ULP on some platforms (macOS clang), so byte-identity is explicitly NOT claimed; cross-platform CI is the arbiter. How each routes to C (zero new symbols):
farrow→composition_of_c. For a fixedmuthe polynomial-in-muFarrow mixer collapses to ONE effective length-4 FIRh_eff[j] = Σ_k mu^k · C[k][j](the Class-N poly-in-mu evaluation), and the fractional-delay output is then the"valid"cross-correlationy[i] = Σ_j h_eff[j]·padded[i+j]of the (one-before / two-after) zero-padded signal withh_eff— a (feed-forward-only) linear convolution re-expressed as a Toeplitz matvec routed through_dsp.correlate_matmul→laplacian.mat_matvec∘mat_matmul→ thec_dispatchedsrmech_dense_matmul_complex, falling back to the complete numpy-free pure_dsp.correlatecascade otherwise. VALUE oracle:mu=0givesh_eff = C[0] = (0,1,0,0)→ exact integer-delay passthrough.sinc_interp→composition_of_c. The complex Whittaker-Shannon band-limit reconstruction is exactly the matvecout = S·ywith the real Class-L kernel matrixS[q][s] = sinc((t_q−t_s)/T)— routed through the numpy-free carriermat_matvec∘mat_matmul→ thec_dispatchedsrmech_dense_matmul_complex(theMatbuffer feeds the kernel zero-copy), elsemat_matmul's numpy-free triple-loop cascade (NOT the rc70dense_matvec_complexnumpy-carrier trap — theMat/Veccarriers are numpy-free). VALUE oracle:target_indices == sample_indicesgivesS = I(sincof nonzero integers is 0) soout == yexactly.wavelet→composition_of_c. Each Haar analysis level —approx[m] = (x[2m]+x[2m+1])/√2,detail[m] = (x[2m]−x[2m+1])/√2— is ONE banded matvec[approx; detail] = H·current(then×nanalysis matrix: topn/2rows the low-pass(+c,+c)band, bottomn/2the high-pass(+c,−c)band,c = 1/√2, the ×2 decimation baked into the row stride) through the samec_dispatchedsrmech_dense_matmul_complex. The transform is orthonormal, so the analysis matrix's transpose is the exact perfect-reconstruction synthesis inverse — the VALUE oracle (inverse ∘ forward ≈ identity).spectral_subtraction→composition_of_c. The forward + inverse transform funnel throughspectral_cascades.fft/.ifft→ thec_dispatchednumeric FFT foundationsrmech_fft_c128(rc139) — the SAME pattern as the rc150wienerop. The observed per-bin magnitude|X|² = re²+im²is the genuine Class-K pin-slot magnitude (rational.sqrtof the real PSD floor for the reconstructed magnitude), NOT Pythonabs()on a complex bin; themax(·)over-subtraction floor is Class-N and the phase is preserved fromXviarational.atan2/cos/sin(numpy-free elementwise glue). VALUE oracle: withnoise_psd ≈ 0a clean signal passes through unchanged.
Why no new C symbol (honest classification). Every substantive kernel already has a C twin: the dense matmul (srmech_dense_matmul_complex, which backs the Toeplitz-matvec convolution + the sinc / Haar matvecs) and the numeric FFT (srmech_fft_c128). The residual per-op work is numpy-free elementwise / integer glue a bare-C host writes inline — so composition_of_c (×4) is the honest classification (zero new C symbols). Each op dispatches to C when the native lib is present and falls back to the complete numpy-free pure path otherwise. 4 rows python_only_debt → composition_of_c; CEIL_PYTHON_ONLY_DEBT 29 → 25; #928 ratchet green. ABI stays 3. New parity test tests/test_sp_interp_wavelet_c_rc151.py (within-tol native == pure for the 4 ops + the VALUE oracles above). BATCH B4 is COMPLETE — 19 sp_transform ops across rc148–rc151. Next per roadmap: B7 (fsk / ofdm / psk_qam modulation) + B9 (qm-numeric bell / gauge / pseudo_hermitian / single_particle) + B10 misc + the remaining sp ops.
[0.9.0rc150]¶
BATCH B4c — sp_transform part 3, wiener / rate-conversion / polyphase: the 5 NUMERIC DSP ops move python_only_debt → composition_of_c (×4) / non_compute (×1) (tools.total stays 403; ABI stays 3; NO new C symbol). B4 = sp_transform (numeric DSP ops that USE the F1 FFT / the dense matmul); B4c = the wiener + multirate + polyphase family (5 ops): closed_form_ops.wiener, path_b_ops.wiener, closed_form_ops.multirate, closed_form_ops.polyphase.op, and closed_form_ops.polyphase.decompose. numpy stays absent; no libm; no abs(). 6 SSOT files rc149 → rc150.
NUMERIC classification (WITHIN-TOL native == pure, NOT byte-identical — the F1-FFT / F2-SVD / B4a / B4b numeric-foundation contract). These are float DSP ops, so the parity test is differential (native == pure to reldiff ≤ 1e-9), NOT byte-equality — an FFT butterfly / convolution matmul can FMA-fuse ~1 ULP on some platforms (macOS clang), so byte-identity is explicitly NOT claimed; cross-platform CI is the arbiter. How each routes to C (zero new symbols):
wiener(closed-form + path_b) →composition_of_c(×2). The two heavy kernels are the forward + inverse transform, and both funnel throughspectral_cascades.fft/.ifft→ thec_dispatchednumeric FFT foundationsrmech_fft_c128(rc139); the per-bin power|X|²=re²+im², the Class-L eps-floormax(·, eps)and the Class-N rational MMSE gainS_xx/(S_xx+S_nn)are numpy-free elementwise glue (noabs()) — the SAMEcomposition_of_cpattern as the B4astft/cross_spectralwindowed-transform ops. The Path B form surfaces the same op as the Class-L cyclic-graph-Laplacian eigenbasis transform (which IS the FFT basis) + the Class-N per-eigenmode gain; D1 algebra-identical to Path A.multirate→composition_of_c. Rational rate conversion is up-sample (zero-insertion, exact) → low-pass convolution → decimate; the one heavy kernel — the convolution — re-expresses the (feed-forward-only) linear filter as a Toeplitz matvec routed through_dsp.convolve_matmul→laplacian.mat_matvec∘mat_matmul→ thec_dispatchedsrmech_dense_matmul_complex. The default windowed-sinc taps are the byte-exact Class-Nrational.sin/rational.cosinteger cascades (C-backed); the zero-insertion / decimation / up-gain are exact integer glue.polyphase.op→composition_of_c. Each polyphase component's convolutionE_k * x_krides the same Toeplitz matvec throughsrmech_dense_matmul_complex(via_dsp.convolve_matmul); the strided split / accumulate / interleave are exact integer-indexed glue.polyphase.decompose→non_compute(honest per-op classification). Splitting a tap table intoLphase branchesE_k[n] = h[k + n·L]is a pure integer reindex (taps[k::L]list-slices + a zero-pad to a multiple ofL); there is NO floating-point kernel — nothing to mirror in C (a bare-C host writesh[k + n·L]as trivial pointer arithmetic), so it honestly carries no owed-C debt.
Why no new C symbol (honest classification). Every substantive kernel already has a C twin: the numeric FFT (srmech_fft_c128), the dense matmul (srmech_dense_matmul_complex), and the Class-N rational.{sin,cos} tap cascades. The residual per-op work is numpy-free elementwise / integer-reindex glue that a bare-C host writes inline — so composition_of_c (4×) / non_compute (1×) is the honest classification (fewest new C symbols: zero). Each compute op dispatches to C when the native lib is present and falls back to the complete numpy-free pure path otherwise. 5 rows python_only_debt → composition_of_c (4) / non_compute (1); CEIL_PYTHON_ONLY_DEBT 34 → 29; #928 ratchet green. ABI stays 3. New parity test tests/test_sp_wiener_rate_c_rc150.py (within-tol native == pure for the 4 compute ops + VALUE oracles: Wiener recovers a clean tone from a noisy observation + Path A ≡ Path B; multirate up-then-down by the same factor ≈ identity to tol; polyphase reconstruction == the direct FIR filter + decompose reindex round-trip). B4 continues: B4d (farrow / sinc_interp / wavelet / spectral_subtraction) — the last B4 rc.
[0.9.0rc149]¶
BATCH B4b — sp_transform part 2, the filter family: the 5 NUMERIC DSP filter ops move python_only_debt → c_dispatched / composition_of_c (tools.total stays 403; ABI stays 3; ONE new C symbol). B4 = sp_transform (numeric DSP ops that USE the F1 FFT / the dense matmul); B4b = the 5 filter (convolution / feedback) ops: closed_form_ops.fir, closed_form_ops.iir, closed_form_ops.allpass, closed_form_ops.matched_filter, and path_b_ops.matched_filter. numpy stays absent; no libm; no abs(). 6 SSOT files rc148 → rc149.
NUMERIC classification (WITHIN-TOL native == pure, NOT byte-identical — the F1-FFT / F2-SVD / B4a numeric-foundation contract). These are float DSP ops, so the parity test is differential (native == pure to reldiff ≤ 1e-9), NOT byte-equality — a convolution matmul / feedback accumulation can FMA-fuse ~1 ULP on some platforms (macOS clang), so byte-identity is explicitly NOT claimed; cross-platform CI is the arbiter. How each routes to C:
fir/matched_filter(closed-form + path_b) →composition_of_c(×3). A (feed-forward-only) linear convolution / cross-correlation is re-expressed as a Toeplitz matvecM·brouted through the new_dsp.convolve_matmul/correlate_matmul→laplacian.mat_matvec∘mat_matmul→ thec_dispatchedsrmech_dense_matmul_complex(the matched filter is a convolution with the reversed-conjugated template). The mode-crop (full/same/valid) is the SHARED helper, value-faithful to the pure_dsp.convolve/correlatecascade. Each dispatches when the native dense-matmul is present and falls back to the complete numpy-free pure cascade otherwise.iir/allpass→c_dispatched(×2), over ONE new C symbolsrmech_iir_lfilter_f64. The recursive difference equationy[n] = Σ b·x[n-k] − Σ a·y[n-k]reads the output the loop is still producing, so it is inherently SEQUENTIAL and does NOT decompose into a matmul / FFT the way a convolution does — it is the minimal genuinely-new numeric kernel the filter family needs (direct-form-I over caller buffers; no scratch arena; JPL-clean, no libm, noabs()).iirdispatches the direct(b, a)form and, for a biquad cascade, dispatches per second-order section;allpassbuilds the mirrored(b_coef, a_coef)pair then dispatches. Both fall back to the complete numpy-free pure difference-equation reference (_lfilter_direct/_lfilter_df1). (The dead lazy-scipy accelerator — scipy needs numpy, which is gone — is removed.)
Debt down + ratchet. 5 rows python_only_debt → c_dispatched (2) / composition_of_c (3); CEIL_PYTHON_ONLY_DEBT 39 → 34; #928 ratchet green. ABI stays 3 (the new srmech_iir_lfilter_f64 is additive; the ctypes shim hasattr-guards it). New parity test tests/test_sp_filters_c_rc149.py (within-tol native == pure for all 5 + VALUE oracles: FIR impulse response = the taps + convolution-length identities; IIR / allpass step + impulse responses; matched-filter peak at the template location + auto-correlation-at-zero-lag = energy). B4 continues: B4c (wiener / multirate / polyphase), B4d (farrow / sinc / wavelet / spectral_subtraction) — the next 2 rcs.
[0.9.0rc148]¶
BATCH B4a — sp_transform part 1, the FFT-spectral family: the 5 NUMERIC DSP transform ops move to composition_of_c (tools.total stays 403; ABI stays 3; NO new C symbol). B4 = sp_transform (19 numeric DSP ops that USE the F1 FFT); B4a = the 5 FFT-spectral ops: closed_form_ops.dct, closed_form_ops.stft, closed_form_ops.spectrogram, closed_form_ops.cross_spectral, closed_form_ops.multitaper. rc139 landed the numeric complex128 FFT foundation srmech_fft_c128 and wired the fft-family (dft/fft/idft/ifft) to it; these 5 transform ops COMPOSE that C FFT (and, for the DCT, the C dense-matmul) but weren't reclassified. numpy stays absent; no libm; no abs(). 6 SSOT files rc147 → rc148.
NUMERIC classification (WITHIN-TOL native == pure, NOT byte-identical — the F1-FFT / F2-SVD numeric-foundation contract). These are float DSP ops, so the parity test is differential (native == pure to reldiff ≤ 1e-9), NOT byte-equality — the FFT-then-window / matmul float accumulations can FMA-fuse ~1 ULP on some platforms (macOS clang fuses a*b+c to one rounding), so byte-identity is explicitly NOT claimed; cross-platform CI is the arbiter (the rc147 ckm FMA lesson, one layer up). How each routes to C (zero new symbols): stft / cross_spectral / multitaper funnel each windowed / tapered frame's transform through spectral_cascades.fft → the c_dispatched srmech_fft_c128 (the SAME composition_of_c pattern as the rc139 fft/ifft/rfft wrappers, one layer up with a Hann/cosine window + |z|² / cross-product / bundle-average elementwise glue — numpy-free list-comps, no abs()); spectrogram composes stft then |z|²; dct routes its cosine-basis matvec M·x through mat_matvec ∘ mat_matmul → the c_dispatched srmech_dense_matmul_complex (the cosine basis is the byte-exact Class-N rational.cos cascade, itself C-backed), the 2· / DCT-III-first-term scaling being trivial glue. Each dispatches to C when the native lib is present and falls back to the complete numpy-free pure path (the pure cexp FFT cascade / the pure triple-loop matvec) otherwise.
Why no new C symbol (honest classification). Every substantive kernel already has a C twin: the numeric FFT (srmech_fft_c128), the dense matmul (srmech_dense_matmul_complex), and the Class-N rational.{cos,sin,sqrt} window/taper cascades. The residual per-op work is numpy-free elementwise glue (window multiply, re²+im² power, bundle average, DCT scaling) that a bare-C host writes inline — so composition_of_c is the honest classification (fewest new C symbols: zero), matching the rc139 fft-wrapper precedent. 5 rows python_only_debt → composition_of_c; CEIL_PYTHON_ONLY_DEBT 44 → 39; #928 ratchet green. ABI stays 3. New parity test tests/test_sp_transform_c_rc148.py (within-tol native == pure for all 5 + VALUE oracles: DCT-II vs a naive cosine sum + the DCT-II∘III round-trip identity dct3(dct2(x)) = 2N·x, per-frame DFT Parseval for STFT + spectrogram == |STFT|², cross-spectral auto-coherence ≡ 1 + auto-CSD real/non-negative, and the multitaper spectral-peak-at-injected-frequency + PSD non-negativity check). B4 continues: B4b (fir/iir/allpass/matched_filter), B4c (wiener/rate/polyphase), B4d (farrow/sinc/wavelet/spectral_subtraction) — ~3 more rcs.
[0.9.0rc147]¶
BATCH B8c — qm_exact_assembly part 3 (CLOSES B8): the 9 gauge / bell / sm / misc qm ops move to composition_of_c (tools.total stays 403; ABI stays 3; NO new C symbol). B8 = qm_exact_assembly (24 exact qm ops), batched by module; B8c = the gauge + bell + sm + single-particle family (9 ops): bell.chsh_pauli_combination / bell.chsh_operator, gauge.casimir_operator / gauge.lie_algebra_residual / gauge.gauge_path_segment / gauge.wilson_loop_from_segments, potentials.harmonic_oscillator_hamiltonian, single_particle.commutator, and sm.ckm_unitarity_residual. Every op is a pure composition of the already-C-backed matrix algebra — the c_dispatched laplacian.mat_matmul (srmech_dense_matmul_complex) + mat_hermitian_eigendecompose + the rc141 C carrier ops srmech_mat_{add,sub,scale} that back the Mat + − * operators + the composition_of_c laplacian.mat_norm + the byte-exact Class-N rational.{sqrt,cos,sin,cexp} integer-cascade C ports. numpy stays absent; no libm; no abs(). 6 SSOT files rc146 → rc147. B8 is now COMPLETE (B8a 6 gamma + B8b 9 so8/triality + B8c 9 gauge/bell/sm = 24 exact qm ops).
Two honest sub-classes (verified EMPIRICALLY, not assumed), both composition_of_c with ZERO new C symbols. (1) 6 BYTE-IDENTICAL. chsh_pauli_combination / chsh_operator / casimir_operator / lie_algebra_residual / harmonic_oscillator_hamiltonian / single_particle.commutator. The native srmech_dense_matmul_complex accumulates the real / imag parts SEPARATELY over p = 0 .. k−1 — bit-for-bit the reduction CPython's complex s += a*b performs — and rational.{sqrt,cos,sin} are byte-exact ports of the pure integer cascades, so the native path is byte-identical to forced-pure EVEN for the irrational-VALUED entries (σ_z⊗σ_z's 1/√2, λ⁸'s 1/√3, the ladder √n). Two are exact-integer (chsh_pauli_combination, commutator on Pauli input); four are float-valued yet byte-identical native == pure (their cells are exact-products like 1/√2·1/√2=½ or single-product, so no FMA-in-a-sum). lie_algebra_residual is exact-0.0 when the algebra holds (SU(2)), ~1e-16 for SU(3), byte-identical either way. The two bell ops route their tensor-sum through the Mat + − * carrier ops + a LOCAL private _kron (Class-I mixed-radix index-addressing, the same composition_of_c shape as so8.an_embedding's local _kron) — NOT the public python_only_debt spectral_cascades.kron. (2) 3 FLOAT-INVARIANT. gauge_path_segment / wilson_loop_from_segments build the Wilson-line holonomy exp(iM) = V·diag(e^{iλ})·Vᴴ through the C-backed mat_hermitian_eigendecompose ∘ the Class-N rational.cexp Euler phase ∘ mat_matmul — non-unique Jacobi eigenbasis, so native vs pure agree on the unitarity invariant + basis-independent holonomy (~1e-9), NOT byte-for-byte. ckm_unitarity_residual joins them: its 3-term V†V accumulation over generic-float CKM entries is FMA-sensitive (macOS clang fuses a*b+c to one rounding; the pure path uses two) — byte-identical on Linux but ~1 ULP on macOS — so it is FLOAT-INVARIANT (native==pure within ~1e-12), NOT byte-exact. Its VALUE is a small ~1e-16 float (unitarity to float precision, NOT exact-zero). (Caught by cross-platform CI — a Linux maxdev-0.0 probe was insufficient.) The Jacobi eigenBASIS is non-unique, so native vs forced-pure agree on the unitarity invariant U·Uᴴ = I and — since exp(iM) is itself basis-INDEPENDENT — on the reconstructed holonomy within the accepted ~1e-9 carrier shift, NOT element-wise byte-for-byte.
Why no new C symbol (honest classification). Every compute step already has a C twin: mat_matmul (srmech_dense_matmul_complex), mat_hermitian_eigendecompose, the srmech_mat_{add,sub,scale} carriers, and the rational.{sqrt,cos,sin} kernels. Re-emitting the tensor constants in C would only risk reproducing Python-literal -0.0 slots (the B8a byte-identity hazard) with no standalone-C gain, so composition is both the honest and the byte-safe classification (fewest new C symbols: zero). 9 rows python_only_debt → composition_of_c; CEIL_PYTHON_ONLY_DEBT 53 → 44; #928 ratchet green. ABI stays 3. New parity test tests/test_qm_gauge_sm_c_rc147.py (6 byte-identical native == pure + 3 float-invariant unitarity/within-tol invariant + the chsh 2√2 / casimir 3-4 / commutator 2iσ_z / HO spectrum / lie-algebra exact-zero value oracles). B8 COMPLETE — next are the numeric batches (B4 sp_transform, B7 modulation, B9 qm-numeric) + B10 misc.
[0.9.0rc146]¶
BATCH B8b — qm_exact_assembly part 2: the 9 so(8) / octonion / triality ops move to composition_of_c (tools.total stays 403; ABI stays 3; NO new C symbol). B8 = qm_exact_assembly (24 exact qm ops), batched by module; B8b = the so(8)/octonion/triality family (9 ops): so8.so8_adjoint_basis / so8.g2_subalgebra / so8.so7_subalgebra / so8.quaternion_subalgebra_stabilizer / so8.an_embedding and triality.triality_swap / triality.triality_automorphism / triality.triality_companions / triality.lean_isa_seventh_primitive. All 9 are standalone-C-reproducible with zero new C symbols. numpy stays absent; no libm; no abs() (Class-K sign lives in the value). 5 SSOT files rc145 → rc146.
Two honest sub-classes (both composition_of_c). (1) 6 BYTE-EXACT. so8_adjoint_basis / g2_subalgebra route their EXACT-INTEGER {−1,0,+1} octonion-derivation matmuls through the c_dispatched laplacian.mat_matmul (srmech_dense_matmul_complex) via the new so8._commutator_c / _matmul_c — integer sums are order-independent in float64, so the native path is byte-identical to forced-pure. triality_swap / triality_automorphism / triality_companions / lean_isa_seventh_primitive compose mat_matmul + the composition_of_c mat_norm over the exact-DYADIC-ℚ companion maps; the companion maps' exact-ℚ RREF-with-free-columns-pinned solve is standalone-reproducible in a bare-C host by the c_dispatched srmech_qmat_rref (QMat.rref) — VERIFIED byte-identical to the fast pure sparse-Fraction solve, which the Python keeps only for speed (the dense 128-unknown srmech_qmat_rref is ~2 s vs the sparse solve's sub-second). All 6 are byte-identical native == forced-pure. (2) 3 FLOAT-COMPOSITION. so7_subalgebra / an_embedding / quaternion_subalgebra_stabilizer compose the C-backed mat_svd (srmech_svd_f64) ∘ mat_hermitian_eigendecompose ∘ mat_matmul ∘ kron — the SAME established composition_of_c pattern as the rc140 esprit / map_ml / mimo_svd float-SVD ops (the "matmul ∘ eig ∘ kron" ledger example). The SVD/eigen basis is non-unique, so native vs forced-pure agree on every invariant — dimension (so(7)=21, g2=14, so(4)=6, the 8+3+3̄ / 1+3+3̄ branching), antisymmetry, span, J² = −I, Killing rank 6, and the basis-independent Killing SPECTRUM within the accepted ~1e-9 carrier shift — and on every EXACT field (the g2-content-addressed MPR attestation, the decomposition tuples), which is what the parity test asserts (element-wise would be wrong for a free SVD basis).
Why no new C symbol (honest classification). Every compute step already has a C twin: mat_matmul (srmech_dense_matmul_complex), mat_svd (srmech_svd_f64), mat_hermitian_eigendecompose, mat_norm, and the exact-ℚ companion solve (srmech_qmat_rref). Re-emitting the octonion/derivation constants in C would only risk reproducing Python-literal -0.0 slots (the B8a byte-identity hazard) with no standalone-C gain, so composition is both the honest and the byte-safe classification (fewest new C symbols: zero). The load-bearing structural certificate is retained: the triality automorphism τ is genuinely order 3 (τ³ = I exact, τ ≠ I, τ² ≠ I — a real permutation of 8v/8s/8c). 9 rows python_only_debt → composition_of_c; CEIL_PYTHON_ONLY_DEBT 62 → 53; #928 ratchet green. ABI stays 3 (no symbol change). New parity test tests/test_qm_so8_triality_c_rc146.py (6 byte-identical native==pure + 3 float-composition invariant/within-tol + the τ order-3 certificate). B8c (gauge + bell + sm + misc, 9 ops) is the next rc — B8 then complete.
[0.9.0rc145]¶
BATCH B8a — qm_exact_assembly part 1: 6 EXACT relativistic / spin Dirac-gamma / Clifford ops move to composition_of_c (tools.total stays 403; ABI stays 3; NO new C symbol). B8 = qm_exact_assembly (24 exact qm ops), batched by module; B8a = the relativistic + spin Dirac-gamma / Clifford family (6 ops). Each builds EXACT matrices (Dirac γ-matrices, Weyl projectors, charge conjugation, Clifford anticommutator residuals) whose entries are Gaussian integers {0, ±1, ±i}, so the native path is byte-identical to forced-pure — no float tolerance, no libm, no abs() (Class-K sign lives in the value). numpy stays absent. 5 SSOT files rc144 → rc145.
The 6 ops are pure compositions of the already-C-backed matrix algebra → composition_of_c (zero new C symbols). relativistic.gamma_5 (γ₅ = i·γ⁰γ¹γ²γ³), relativistic.weyl_left_projector / weyl_right_projector (P_{L,R} = (I ∓ γ₅)/2), relativistic.charge_conjugation_matrix (C = i·γ²γ⁰), relativistic.clifford_residuals ({γ_μ,γ_ν} − 2η_{μν}I, γ₅² − I, {γ₅,γ_μ} residual norms), and spin.pauli_clifford_residuals ({σ_i,σ_j} − 2δ_{ij}I, [σ_i,σ_j] − 2iε_{ijk}σ_k residual norms). Every matrix operation these compose is C-backed: the product chains run through the c_dispatched laplacian.mat_matmul (srmech_dense_matmul_complex); the scalar-scale / add / subtract now route through the rc141 C carrier ops srmech_mat_scale / srmech_mat_add / srmech_mat_sub (the Mat *, +, − operators — this rc flips relativistic._scale / _mat_add / _mat_sub off the pure list-comprehension helpers onto the carrier so the native path runs the elementwise algebra in C too); the residual norms run through the composition_of_c laplacian.mat_norm; and the base γ / Pauli constant builders (gamma_matrices, pauli_matrices, pauli_identity) are already composition_of_c. The Clifford / Pauli residuals stay exact-zero (the algebra identity holds).
Why no new C symbol (honest classification, not a shortcut). The task's own decision tree yields composition_of_c when the base γ-matrices already have a C-composable source — and they do (they are already composition_of_c standalone-ready constant data). Re-emitting the γ / Pauli constants through a new srmech_qm_* C symbol would only have to reproduce the Python literals' negative-zero slots (-1j gives re = -0.0; _scale(-1.0, ·) on a zero entry gives -0.0) to stay byte-identical, a fragile sign-of-zero hazard with no standalone-C gain, so composition is both the honest and the byte-safe classification. 6 rows python_only_debt → composition_of_c; CEIL_PYTHON_ONLY_DEBT 68 → 62; #928 ratchet green. ABI stays 3 (no symbol change). New byte-identical parity test tests/test_qm_gamma_c_rc145.py (native == forced-pure for all 6, EXACT, incl. the exact-zero residuals). B8b (so8 + triality, 9 ops) + B8c (gauge + bell + sm + misc, 9 ops) are the next 2 rcs.
[0.9.0rc144]¶
Batch B6b (sp_coder_dp part 2) — 4 harder coder/DP C peers (C:Python parity backfill). Adds C peers for arithmetic_coding (integer range coder → srmech_arithmetic_encode), lz77 (sliding-window match → srmech_lz77_encode), viterbi (log-prob trellis DP → srmech_viterbi), and mlse (channel-equalizer trellis → srmech_mlse). Each is byte-identical to its Python kernel (exact for the integer coders; deterministic-same-order for the float trellis DP — identical accumulation order + argmin tie-break). Dispatch is hasattr-guarded; the pure fallback stays byte-identical. jpeg DEFERRED — it is a float-DCT numeric op, not exact, so it belongs in a later differential-tested numeric batch, not this byte-identical one (honest classification, not forced into the exact batch). 4 rows python_only_debt → c_dispatched; CEIL_PYTHON_ONLY_DEBT 72 → 68; #928 ratchet green. ABI stays 3 (additive symbols). JPL-clean (≤60-line funcs, ≥2 asserts, no goto/malloc/abs/recursion/libm). numpy-absent.
[0.9.0rc143] - 2026-07-06¶
BATCH B6a — sp_coder_dp part 1: 5 EXACT signal-processing coder / quantizer C peers (tools.total stays 403; ABI stays 3). The compute batches continue dropping the python_only_debt: B6 = sp_coder_dp (10 exact coder/DP ops), split by complexity into two rcs. B6a = the 5 simpler EXACT ops — memoryless quantizers + run-length + Huffman — that had NO C peer. Byte-identical C: these are integer / exact coders (entropy codes, codebook lookup, run-length, threshold quantize), so there is no float tolerance, no libm, and no abs() (Class-K sign is a pin-slot boundary, never abs()). numpy stays absent. 5 SSOT files rc142 → rc143.
(1) The 5 ops → 4 new C symbols (the two sign_quantise paths share one twin). closed_form_ops.sign_quantise.op + path_b_ops.sign_quantise.op → srmech_sign_quantise (Class-K {−1,0,+1} threshold projection; dead_band ≤ 0 two-level in ≥ threshold ? +1 : −1, dead_band > 0 three-level with the acceptance band — the threshold IS the sign boundary, no abs()); closed_form_ops.vector_quantisation.op (encode) → srmech_vector_quantise_encode (Class-K nearest-code argmin by SQUARED Euclidean distance accumulated left-to-right in a double → bit-identical fold; ties → LOWEST index via strict <, never a float abs); closed_form_ops.rle.op (encode) → srmech_rle_encode ((symbol, count) run records; a run stops at a symbol change or at max_run); closed_form_ops.huffman.op (encode) → srmech_huffman_build_codes (canonical prefix codes; the tree build reproduces the Python heapq (freq, counter) node ordering EXACTLY — leaves carry a first-appearance counter, each merge pops the two smallest (freq, counter) nodes, first-popped → bit 0 / second → 1, and out_order mirrors the left-first code-dict key order — so each symbol's code AND the code-dict order match the pure build).
(2) BYTE-IDENTICAL (native == forced-pure, EXACT). Integer / exact coders → the C is bit-for-bit the pure-Python kernel, no numeric tolerance. Same-rc C, caller-arena (huffman uses fixed ≤512-node local scratch; no malloc), JPL Power-of-Ten clean (≤60-line funcs, ≥2 asserts, no goto/malloc/abs, no recursion — the huffman code walk is a leaf→root parent-pointer walk + an explicit-stack DFS), pedantic -Werror -Wpedantic clean in BOTH -O2 asserts-live AND -DNDEBUG. ABI stays 3 (additive symbols; the ctypes shim hasattr-guards every binding, so a pre-rc143 lib doesn't AttributeError). The inverse gathers (rle / huffman / vq decode) stay the trivial pure-Python replay/lookup (no compute kernel).
(3) DISPATCH WIRED + debt DOWN 5 — python_only_debt 77 → 72. Each Python op dispatches its forward/compute path to its C peer when HAS_NATIVE and falls back to the byte-identical pure kernel (the complete alternative for no-C / pre-rc143 hosts). All 5 rows leave the DEBT bucket → c_dispatched; the #928 down-only ratchet ceiling CEIL_PYTHON_ONLY_DEBT lowered 77 → 72. tools.total stays 403 (existing ops gaining C, no new ToolEntry). Correctness (tests/test_coder_c_rc143.py, numpy-absent): each of the 5 ops is byte-identical to its forced-pure kernel across sweeps (sign_quantise with/without dead-band + both paths; VQ ties→lowest-index + single-vector; RLE max_run splits + long runs; Huffman empty / single-symbol / degenerate-frequency trees with exact code-string + code-dict-order match; encode→decode round-trips). B6b (arithmetic_coding, lz77, viterbi, mlse, jpeg — 5 harder) is the NEXT rc.
[0.9.0rc142] - 2026-07-05¶
BATCH B1 — hdc_klein4_exact: the FIRST compute batch (the 4 foundations are done; tools.total stays 403; ABI stays 3). With the four C:Python-parity FOUNDATIONS shipped (the_one / fft / svd / carriers), the compute batches begin dropping the python_only_debt. B1 is the cheapest: 9 EXACT Klein-4 / BSC ops that had NO C peer but sit over the ALREADY-C srmech_hdc / srmech_klein4 foundation. Byte-identical C — integer / sector ops, NO numeric tolerance. numpy stays absent; no abs() (Class-K sign is a pin-slot, never abs()). 5 SSOT files rc141 → rc142.
(1) The 9 ops + their C peers (6 new symbols over the srmech_hdc/klein4 foundation). hdc.bundle_with_ties → srmech_hdc_bundle_with_ties (BSC per-bit majority + the exact-tie Class-K event surfaced, any n_vectors); the three chirality flips klein4_chirality_flip_gamma5 / klein4_chirality_flip_omega7 / klein4_cpt_mirror → srmech_klein4_sector_flip (XOR every element with a constant sector mask 2 / 1 / 3); klein4_sector_count → srmech_klein4_sector_count (per-sector occupancy [n0,n1,n2,n3]); klein4_holographic_encode → srmech_klein4_holographic_encode (replica-major replication); klein4_holographic_decode → srmech_klein4_holographic_decode (blind per-position majority erased=NULL + known-location first-survivor); klein4_triality_encode → srmech_klein4_triality_encode (the order-3 orbit [v,T(v),T²(v)]); klein4_triality_correct → srmech_klein4_triality_correct (the 2-of-3 triality majority over [b0, T⁻¹(b1), T(b2)]). The last two COMPOSE the existing srmech_klein4_triality_cycle in C (everything-mirrors); a shared srmech_k4_argmax4 static helper is the ties→lowest majority read-out for both the blind decode and the corrector.
(2) BYTE-IDENTICAL (native == forced-pure, EXACT). These are integer / sector ops, so the C is bit-for-bit the pure-Python array('B') kernel — no float, no tolerance. Same-rc C, caller-arena (triality_correct takes a 2·D scratch), JPL Power-of-Ten clean (≤60-line funcs, ≥2 asserts, no goto/malloc/abs), pedantic -Werror -Wpedantic clean in BOTH -O2 asserts-live AND -DNDEBUG. ABI stays 3 (additive symbols; the ctypes shim hasattr-guards every binding, so a pre-rc142 lib doesn't AttributeError).
(3) DISPATCH WIRED + debt DOWN 9 — python_only_debt 86 → 77. Each Python op dispatches to its C peer when HAS_NATIVE and falls back to the byte-identical pure kernel (the complete alternative for no-C / pre-rc142 hosts). All 9 rows leave the DEBT bucket → c_dispatched; the #928 down-only ratchet ceiling CEIL_PYTHON_ONLY_DEBT lowered 86 → 77. tools.total stays 403 (existing ops gaining C, no new ToolEntry). ABI stays 3. Correctness (tests/test_hdc_klein4_c_rc142.py, numpy-absent): each of the 9 C peers is byte-identical to its forced-pure kernel across a size sweep (blind + known-erasure decode, even + odd bundle counts, all three flip masks, encode/correct round-trips, and the unrecoverable all-erased ValueError path).
[0.9.0rc141] - 2026-07-05¶
FOUNDATION F0 — Carriers-C: the Mat/Vec CARRIER struct + ctor/accessor/elementwise/lifecycle C API (the LAST C:Python-parity backfill foundation; tools.total stays 403; ABI stays 3). The numeric compute KERNELS were already C (srmech_dense_matmul_complex / srmech_svd_f64 / srmech_fft_c128 …) and read the Python Mat/Vec array('d') buffers ZERO-COPY — but the carrier OBJECT (construction, get/set, row/col views, elementwise arithmetic, .conj/.T, buffer sizing/lifecycle) lived ONLY in Python (mat.py/vec.py), so a bare C host (a microcontroller, a no-Python embed) could call the kernels but could not HOLD or MANIPULATE a carrier. This rc gives the C host the carrier vocabulary — build a carrier, index it, do elementwise math, conjugate/transpose, then feed the SAME buffer straight to the kernels — with no Python present. The everything-mirrors capstone. numpy stays absent; no abs() (conj/neg are Class-K sign flips); no libm. 5 SSOT files rc140 → rc141.
(1) The carrier struct (c/src/srmech_carrier.c). srmech_mat_t { double *buf; uint32_t rows, cols; int is_complex } + srmech_vec_t { double *buf; uint32_t n; int is_complex } — a VIEW over a CALLER-OWNED buffer (JPL Rule 3: no malloc, the same caller-arena discipline as srmech_bigint_t's limbs). Row-major; one double per real element / interleaved (re,im) per complex element (= C99 double _Complex), so buf is byte-identical to the Python carrier's array('d') and feeds the interleaved-complex kernels no-copy. The API: sizing (srmech_mat_buf_len/srmech_vec_buf_len), construction (srmech_{mat,vec}_init view + srmech_{mat,vec}_zeros view+clear), accessors (srmech_{mat,vec}_get/set + srmech_mat_row/col views), elementwise binary (srmech_{mat,vec}_{add,sub,mul}; * is Hadamard), scalar broadcast (srmech_{mat,vec}_{scale,add_scalar}), unary (srmech_mat_{conj,neg,transpose} + srmech_vec_{conj,neg}), and the zero-copy kernel BRIDGE srmech_mat_matmul_c128 (feeds the three carrier buffers straight to srmech_dense_matmul_complex).
(2) BYTE-IDENTICAL to the Python carrier. Every value op computes with the SAME IEEE-754 operation order CPython uses: complex multiply is the naive _Py_c_prod (ac−bd) + (ad+bc)i, add/sub componentwise — so the C result is bit-for-bit the Python Mat/Vec result. (Complex / is deliberately NOT in the C surface: CPython uses Smith's scaled algorithm, so the carrier's / stays the pure-Python path — the complete + byte-exact oracle.) Same-rc C, JPL Power-of-Ten clean, pedantic -Werror clean in BOTH -O2 asserts-live and -DNDEBUG. ABI stays 3 (additive symbols; the Python ctypes shim hasattr-guards every binding).
(3) DISPATCH WIRED (pure fallback identical). The Python Mat/Vec whole-buffer compute methods route to the C carrier ops when HAS_NATIVE (_native.has_native_carriers()): conj/__neg__/transpose (unary) and the two unambiguous elementwise shapes — a same-shape/same-length carrier operand (direct C op) and a scalar (C scalar broadcast) — over the SAME array('d') buffer zero-copy; every other operand (reflected sub/div, 2-D-sequence coercion, cross-rank) falls to the unchanged pure _elementwise. The pure-Python carrier is the COMPLETE alternative (numpy-absent / no-C hosts) and the byte-exact oracle.
Correctness (tests/test_carriers_c_rc141.py, numpy-absent + a standalone c/test/test_srmech_carrier.c bare-C-host smoke). The C-constructed carrier is BYTE-IDENTICAL to the Python Mat/Vec for get/set + elementwise (add/sub/mul/scale/conj/transpose) across real + complex cases; the C carrier feeds srmech_dense_matmul_complex zero-copy and matches the pure mat_matmul; srmech_mat_buf_len agrees with the carrier's array('d') length. Complex128 needs ≈ 0 work — it already wraps a Python complex (two float64) = C99 double _Complex, the same interleaved (re,im) the carriers speak; and HV ops already have C peers (srmech_hdc / srmech_klein4) — both confirmed + skipped (no rebuild). Ledger: the carrier object methods are class methods, NOT module-level public compute ops, so they are NOT Rosetta ledger rows — the debt bucket is untouched and CEIL_PYTHON_ONLY_DEBT stays 86 (the value here is the python-free carrier VOCABULARY / everything-mirrors, not a debt move). tools.total stays 403 (no new ToolEntry — a C struct + API a bare host uses, not a new public op). ABI stays 3.
[0.9.0rc140] - 2026-07-05¶
FOUNDATION F2 — srmech_svd_f64 + srmech_qr_f64: the NUMERIC f64 SVD + QR C kernels (the C:Python-parity backfill; tools.total stays 403; ABI stays 3). The C surface had jacobi_eigvals / hermitian_eigendecompose_ws / dense_matmul / dense_solve but NO SVD and NO QR, so the subspace / MIMO / LA family (matrix_cascades.{qr,svd,lstsq,eigvals} + the signal_processing subspace ops) was python-only. This rc builds the two numeric-LA foundations they dispatch to. numpy stays absent; no abs()/fabs() (every magnitude is a Class-K sign-branch); no libm (the only root is the Class-N srmech_rational_sqrt). 5 SSOT files rc139 → rc140.
(1) srmech_qr_f64 — Householder QR (direct). A = Q·R, A m×n row-major, Q m×m orthogonal, R m×n upper-trapezoidal. DIRECT (no iteration): the product of min(m,n) reflectors H = I − β·v·vᵀ; the phase α = −sign(x0)·‖x‖ is a Class-K pin-slot (a sign-BRANCH, never fabs). The reflector vector is bump-carved from the caller arena (srmech_qr_f64_ws_bound).
(2) srmech_svd_f64 — one-sided-Jacobi SVD (iterative; the audit's weakest link). A = U·diag(S)·Vᵀ (m≥n) via one-sided Jacobi (Hestenes) — chosen over Golub–Kahan bidiagonalisation for a malloc-free JPL-clean kernel: a naturally-bounded SWEEP loop, globally convergent, high relative accuracy on the small singular values. Rank-deficient robustness (Demmel–Veselić): a near-dependent column falls into a geometric-shrink cycle (its cosine to a sibling stays ≈ 1 as its norm decays toward 0), so an angle-only stopping test would rotate it forever → hit the cap → return NOT-CONVERGED on ORDINARY rank-deficient inputs (fatal on a python-free host with no fallback). The fix: a numerical-zero column-norm² floor relative to the Frobenius norm (‖col‖² ≤ ‖A‖_F² · 1e-30 ⇒ a σ ≈ 0 column, skipped) — this breaks the cycle at ~sweep 2 (far above denormal range) so every rank-deficient matrix converges to a valid, reconstructing result. THE CONVERGENCE CONTRACT (kept as the last-resort safety): an EXPLICIT sweep cap (SVD_MAX_SWEEPS = 60, JPL Rule 2 bounded loop). A sweep that rotates NO pair is converged → SRMECH_OK; hitting the cap with a rotation still pending returns SRMECH_ERR_OVERFLOW (a NOT-CONVERGED status, NEVER a silent wrong answer), so the Python dispatch falls back to the pure Gram-eigen SVD — but this now fires only on genuinely pathological / huge inputs, NOT on ordinary rank-deficient ones. The working copy + rotation accumulator are caller-arena (srmech_svd_f64_ws_bound). Both kernels are same-rc C, JPL Power-of-Ten clean, pedantic -Werror clean in BOTH -O2 asserts-live and -DNDEBUG. ABI stays 3 (additive symbols).
Correctness (differential, independent reference, numpy-absent): QR — reconstruction A==Q·R ≤ 3.1e-15, orthogonality QᵀQ==I ≤ 8.9e-16, R upper-trapezoidal ≤ 2.0e-15 across square / tall / wide-columns shapes. SVD — reconstruction U·diag(S)·Vᵀ==A ≤ 1.2e-14, VᵀV==I ≤ 4.0e-15, UᵀU==I ≤ 8.7e-15, descending σ≥0, and S² vs an INDEPENDENT eigendecomposition of AᵀA (srmech_jacobi_eigvals) ≤ 5.7e-14; the near-degenerate stress (σ = 5, 3.0000001, 3.0, 0.5) recovers all four exactly (recon 8.9e-16), and every rank-deficient input yields an exact σ=0 with orthogonality preserved — including the two geometric-shrink regression matrices [[1,2,3],[2,4,6],[1,1,1]] and [[0,0,0],[1,2,3],[2,4,6]] that hit the cap before the Demmel–Veselić floor (0/40 random full-rank + 6/6 rank-deficient now return valid results, recon ≤ 1.8e-15). The convergence contract is still proven (a cap=1 build returns rc=4 = NOT-CONVERGED on a matrix needing more sweeps → pure fallback, no silent wrong answer) — it just no longer fires on ordinary rank-deficient inputs. tests/test_svd_qr_f64_rc140.py.
(3) DISPATCH WIRED + debt DOWN — python_only_debt 93 → 86. mat_svd's REAL (m≥n) path routes to srmech_svd_f64 (a directly-computed SVD that does NOT square κ, vs the Gram AᴴA eigen-route it keeps for complex / m<n / not-converged); matrix_cascades.qr real input routes to srmech_qr_f64; matrix_cascades.lstsq real overdetermined input routes to the QR least-squares solve (R x = Qᵀb, Golub §5.3.3, more stable than the normal equations). Seven rows leave the DEBT bucket: matrix_cascades.{qr,lstsq} → c_dispatched (×2); matrix_cascades.{svd,eigvals} + closed_form_ops.{esprit,map_ml,mimo_svd} → composition_of_c (×5, composing the now-C-backed mat_svd / mat_eigvals / mat_hermitian_eigendecompose / mat_solve). matrix_cascades.einsum (tensor contraction, not LA), beamforming_fixed (delay-and-sum), and ica_jade (its dominant JADE Givens joint-diagonalisation is a pure-Python kernel with no C twin) honestly stay python_only_debt. The #928 down-only ratchet ceiling CEIL_PYTHON_ONLY_DEBT lowered 93 → 86. tools.total stays 403 (no new ToolEntry — existing ops gaining C). ABI stays 3.
[0.9.0rc139] - 2026-07-05¶
FOUNDATION F1 — srmech_fft_c128: the NUMERIC complex128 FFT/IFFT C kernel (the C:Python-parity backfill #743/#747; tools.total stays 403; ABI stays 3). The C:Python-mirror audit found the C surface had NO numeric complex FFT — only the exact-integer srmech_exact_dft_i64 (ℤ[ζ_N]) + srmech_autocorrelation_f64. So the whole signal_processing fft-family (dft/fft/idft/ifft + every transform that USES an FFT) was python-only. This rc builds the numeric FFT foundation the fft-family dispatches to. numpy stays absent; no abs() (the FFT reads no magnitude); no libm (twiddles are the Class-N srmech_cos/srmech_sin). 5 SSOT files rc138 → rc139.
(1) srmech_fft_c128 — the numeric FFT/IFFT, arbitrary N. The new srmech_fft_c128 (c/src/srmech_fft.c) transforms an interleaved-(re,im) length-2n double buffer (the Complex128/Vec carrier layout → zero-copy from the carriers). A power-of-two n runs the ITERATIVE (no-recursion, JPL Rule 1) radix-2 Cooley–Tukey butterfly over a bit-reversed copy, twiddles strided from ONE e^{s·2πi·k/N} table; arbitrary / PRIME n runs Bluestein's chirp-z (the size-N DFT becomes a size-M circular convolution, M = next pow2 ≥ 2N−1, via three radix-2 FFTs) — so it is NOT power-of-2-only. inverse != 0 applies the single 1/N scale (matching NumPy ifft + the pure-Python cascade). NUMERIC (FPU-tol), NOT byte-exact — contrast the exact-integer twin. pi is the exact Q61 half-pi anchor (double-projected); the twiddle / chirp trig is the libm-free srmech_cos/srmech_sin. Standalone-complete: all scratch (twiddle table + Bluestein buffers) is bump-carved from the CALLER arena ws (no malloc), sized by the new srmech_fft_c128_ws_bound(n). Same-rc C, JPL Power-of-Ten clean, pedantic -Werror clean in BOTH -O2 asserts-live and -DNDEBUG. ABI stays 3 (additive symbols srmech_fft_c128 + srmech_fft_c128_ws_bound).
Correctness (differential, independent reference): forward matches a naive O(N²) DFT (stdlib cmath) to ≤ 7.3e-14 across N up to 257 including prime N (Bluestein: 3, 5, 7, …, 251, 257); FFT(IFFT(x)) == x to ≤ 1.6e-15. tests/test_fft_c128_rc139.py.
(2) DISPATCH WIRED. spectral_cascades.dft / .fft route their FLOAT path through _native.fft_c128_c(...) when HAS_NATIVE, falling back to the pure cexp cascade (the COMPLETE alternative + parity oracle) otherwise. The exact-integer _exact_transform path (Gaussian-integer power-of-two → srmech_exact_dft_i64) still takes precedence, so integer signals stay bit-exact. The closed_form_ops + path_b_ops fft/ifft/rfft funnel through that C-dispatched core. ctypes binding is hasattr-guarded in _native.py. Dispatched-to-C matches the pure path to ≤ 1e-10 (differential, native forced OFF via monkeypatch).
(3) THE fft-family DEBT DROPS — python_only_debt 103 → 93. Ten fft-family rows leave the DEBT bucket: spectral_cascades.{dft,fft} → c_dispatched (×2, they route to srmech_fft_c128); spectral_cascades.{idft,ifft} + closed_form_ops.{fft,ifft,rfft} + path_b_ops.{fft,ifft,rfft} → composition_of_c (×8, they compose the C-dispatched core). (spectral_cascades.kron is a Kronecker product, NOT an FFT — it stays python_only_debt.) The #928 down-only Rosetta ratchet ceiling CEIL_PYTHON_ONLY_DEBT is lowered 103 → 93 (debt DOWN, the whole point). tools.total stays 403 (no new ToolEntry — existing ops gained C).
[0.9.0rc138] - 2026-07-05¶
FOUNDATION F3 — srmech_the_one: the S(σ,θ) ADJOINT generator gets its C peer (the FLAGSHIP C:Python-parity backfill #743; tools.total stays 403; ABI stays 3). The C:Python-mirror audit found the S(σ,θ) generator (cascade/one.py — the octonion-epicycle ADJOINT) was pure-Python with NO C peer, PARKED in the non-debt bignum_reference bucket to dodge the everything-mirrors ratchet (exactly the pattern rc137 caught). This rc builds its C peer + fixes the taxonomy honesty. numpy stays absent; no abs() (σ is the Class-K pin/sign). 5 SSOT files rc137 → rc138.
(1) srmech_the_one — the adjoint in C, EXACT + BYTE-IDENTICAL. The new srmech_the_one (c/src/srmech_the_one.c) COMPOSES the existing exact-rational bignum series peers srmech_cos_series_truncate_big / srmech_sin_series_truncate_big (the same Class-N Taylor partials Python builds cos/sin from) with a block-tiling / Fano-orientation kernel — producing the SAME 14 exact adjoint rationals One.to_flat_rational returns, BYTE-IDENTICAL to Python at ANY magnitude (over caller-arena srmech_bigint, NO float, NO libm, NO malloc). The ℍ Fano plane (1,2,+1) places σ·cos/σ·sin; 𝕆's (1,6,-1) places σ·cos and -σ·sin (the fixed Cayley–Dickson orientation, Baez 2002 §2). It is w-BLIND — the winding folds away in the adjoint base (the rc137 winding surface srmech_winding.c is SEPARATE + unaffected; the C peer never receives w). Same-rc C, malloc-free, JPL Power-of-Ten clean, pedantic -Werror clean in BOTH -O2 asserts-live and -DNDEBUG. ABI stays 3 (additive symbols srmech_the_one + srmech_the_one_ws_bound).
(2) DISPATCH WIRED. the_one() builds its adjoint via _native.the_one_c(σ, θ_num, θ_den, terms) when HAS_NATIVE (reshaping the 14 native rationals into the three Hurwitz Blocks), falling back to the pure tiling (the COMPLETE alternative + parity oracle) otherwise. One.to_flat_rational reads those blocks; One.to_matrix / One.to_scalar recover cos/sin from the stored adjoint (σ·cos at blocks[1].imag[0], σ·sin at imag[1]; un-applied via Class-K/C _chiral_scale) — so all three route through the C peer. ctypes binding is hasattr-guarded in _native.py. Python == C BYTE-IDENTICAL (exact rational, not a float tol; verified over several σ / θ in tests/test_the_one_c_foundation_rc138.py). The winding readouts + spinor stay w-invariant and UNAFFECTED (to_matrix(w=(5,2,9)) == to_matrix(w=(0,0,0))).
(3) THE 3-ROW LEAK FIXED — bignum_reference → c_dispatched. cascade.one.{the_one, one_matrix, to_scalar} now have a C peer, so they move straight to c_dispatched (they never touched the debt bucket → the debt ceiling is UNCHANGED). qm.hurwitz Rosetta parity + one.toml / test_one_rc49 / test_hurwitz_rc50 / test_one_class_catalog_rc138 pass UNCHANGED (now dispatching to C, SAME values).
(4) TAXONOMY HONESTY — the python_only_irreducible → python_only_debt rename + the bignum_reference guard. The DEBT bucket python_only_irreducible is renamed python_only_debt EVERYWHERE (the honest name for a monotone-decreasing OWED-C-mirror debt bucket) — a mechanical find-replace with UNCHANGED semantics + ceiling (still 103). AND a NEW ratchet guard (test_bignum_reference_rows_are_justified) asserts every bignum_reference row carries EITHER an explicit oracle_justification field OR a c_companion c_dispatched path — else it hides compute from the ratchet (as the_one did). All 30 genuine oracles now carry an oracle_justification; the guard closes the hiding-spot.
[0.9.0rc137] - 2026-07-05¶
the_one carries the WINDING w — the metacycle-fold #1276 fix (siona gh#1276; same-rc BYTE-IDENTICAL C peer; tools.total stays 403; ABI stays 3). rc136's propagate seam-fold "discards the winding w — folds to one seam side, the epicycle harvest; carrying w to expose the metacycle harvest is a separate rc, #1276". That rc is this one, on the substrate generator: the_one(σ, θ) stored the ADJOINT cosθ·e₁ + sinθ·e_b (full-angle, 2π-periodic → w-BLIND: it FOLDS the winding away, addressing only the epicycle — one side of the seam). rc137 lifts SO→Spin (the double cover): it STORES the spinor form + carries the winding TRIAD WHOLE, with a divmod binary-tower chirality readout (NOT σ = w mod 2). No new ToolEntry (the readouts are One methods + the re-exported winding_tower carrier surface — exempt like to_scalar/one_*), so tools.total stays 403. numpy stays absent; no abs() (σ is the Class-K pin/sign). 5 SSOT files rc136 → rc137.
(1) STORE the SPINOR (half-angle) state. One now carries spinor = ((−1)^Σw·cos(θ/2), σ·(−1)^Σw·sin(θ/2)) — the half-angle, 4π-periodic object (built from the SAME Class-N cos_series_truncate/sin_series_truncate at θ/2, exact-rational). So the state shows the winding INTRINSICALLY: a genuine double-cover / Möbius object where ONE full winding flips the spinor sign (q → −q), TWO restore it (q → q) — 2 windings = 1 identity cycle at 4π. spinor_sign reads (−1)^Σw (±1, the Spin→SO 2:1 lift; correct double-cover physics on the sign while w is carried WHOLE).
(2) w = a TRIAD, carried WHOLE. w = (w_saros, w_metonic, w_callippic) — three whole-ℤ windings filling the three INERT +3-grammar ℝ·1 anchors (B/H/N, the Antikythera back-panel dials R31 confirmed are the projection-enablers). One fast epicycle θ + 3 nested metacycle dials. Each component is a FULL ℤ — never % 2.
(3) THE DIVMOD-GRADING CORRECTION — NOT σ = w mod 2. w mod 2 is the quotient map (ℤ→ℤ/2): it keeps the parity bit but THROWS THE CARRY AWAY + MELDS different windings (w=5 ≡ w=7 ≡ 1) — re-introducing the exact fold #1276 exists to avoid. So each w component is carried WHOLE, and chirality is a READOUT of w's BINARY TOWER via divmod: the new winding_tower(w) -> tuple[bits] recurses divmod(w,2)=(carry,bit) (keeps BOTH — the ℤ/2 GRADING: bit = the σ at this doubling level, carry = the retained 2q), giving the LSB-first binary expansion = the (ℤ/2)^d hypercube / Cayley–Dickson doubling coordinate (unbounded past the Hurwitz n≤3 rung). The anti-collapse proof: winding_tower(5) == (1,0,1) and winding_tower(7) == (1,1,1) are DISTINGUISHED — NOT melded (the grading is KEPT). One.sigma_effective() reads σ modulated by the parity of the FULL popcount across the triad's towers (every graded bit counts) — so it too distinguishes w=5 (popcount 2 → σ) from w=7 (popcount 3 → −σ), where the forbidden bare w mod 2 would collapse them.
(4) ROSETTA-PRESERVING ADJOINT PROJECTION. to_matrix / to_flat_rational stay the ADJOINT PROJECTION of the spinor state — the spinor→SO double-cover image q → R(q) = the full-angle rotation, which is w-INVARIANT (R(−q) = R(q); the winding folds away in the base, fiber-spatially-absent). Computed from σ, θ exactly as the pre-winding the_one, so the 14 exact rationals + the 14×14 Mat are rational-identical for w=(0,0,0) AND every θ — AND identical across every winding (proven w-invariant). The qm.hurwitz Rosetta parity + ALL existing consumers (one.toml, test_one_rc49, test_hurwitz_rc50, test_one_class_catalog_rc138) pass UNCHANGED.
(5) NEW readouts (the winding surface, from the spinor total space). winding (the triad, whole), winding_tower() (the per-component divmod binary towers), sigma_effective() (via the tower, not a bare parity), spinor_sign ((−1)^Σw), trace_spinor() (the half-angle character 2·(−1)^Σw·cos(θ/2) — the double-cover read a full-angle object can't show), and unwrapped_phase() (per-metacycle-scale LOSSLESS 2π·w_k + θ — the metacycle crank a θ-only object can't reach; π stays a cascade, the residual θ exact; reaches the N=2 doubled-period / subharmonic — the EPH #1274 resolvent tie). All JSON-native. New tests/test_the_one_winding_rc137.py.
SAME-RC C PEER (srmech_winding.c) — BYTE-IDENTICAL (exact-integer, not a float tol). The winding ops are integer/rational and do NOT depend on the un-mirrored S(σ,θ) adjoint generator (they never touch the adjoint — that generator's C peer stays a separate, pre-existing owed item). So they earn their C peer THIS rc (the everything-mirrors / same-rc discipline): four NEW exported symbols — srmech_winding_tower (the divmod-recursive LSB-first bit tower of |w|), srmech_sigma_effective (σ × parity of the FULL popcount over the triad's towers), srmech_spinor_sign ((−1)^Σw), and srmech_unwrapped_phase (the metacycle turns). Caller-supplied buffers, malloc-free, no abs() (σ = Class-K sign; a retrograde winding is the Class-C reversal -w via defined unsigned wrap — no INT64_MIN UB), JPL Power-of-Ten clean, pedantic -Werror clean in BOTH -O2 asserts-live and -DNDEBUG. Python == C BYTE-IDENTICAL (exact integers → no float tolerance; the pure path is the arbitrary-precision alternative for a winding beyond int64). ctypes bindings are hasattr-guarded in _native.py; winding_tower moves to c_dispatched in the Rosetta ledger. ABI stays 3 (additive symbols only). winding_tower is exempt in the tool-schema coverage gate (a carrier surface reachable via Python, like to_scalar/one_*).
[0.9.0rc136] - 2026-07-05¶
EPH — the complex-time Wick-rotation propagator, via two NEW propagate / eph_harvest ops (siona gh#1274; +2 ops; ABI stays 3). EPH = harvest = Propagate·excite generalises the op⊗operand pattern to a full retrieval / inference cascade — a propagator P = e^{-zL} (operator) applied to an excitation u0 (operand) → the harvest H. tools.total 401 → 403 (the two NEW ToolEntries). ABI stays 3 (two NEW exported symbols — additive). numpy stays absent; no abs() (Class-K magnitude / Class-C sign). 5 SSOT files rc135 → rc136.
Thermal and coherent are ONE op, not two — the WICK-ROTATION correction. The thermal e^{-tL} and the coherent e^{-itL} are NOT two ops: they are the ONE complex-time propagator e^{-zL} with z COMPLEX, the i being the Wick-rotation phase. arg(z) is the coherence dial — z real → thermal diffusion (decoherent; real damping e^{-tλ}), z imaginary → coherent unitary quantum walk (‖harvest‖ = ‖u0‖ conserved), arg(z) BETWEEN → PARTIAL coherence (z = t·e^{iφ}, φ ∈ [0, π/2] the dial) — the real chloroplast regime ONLY the unified form can name (two separate thermal / coherent ops cannot express the middle; measured monotonically 0.37 → 0.40 → 0.49 → 0.68 → 1.00 across the dial). RBS-SNN = EPH-with-a-synaptic-propagator (P = connectome / weight matrix); the neuron is one propagator choice, no privileged instance. Composes the framework's Class-L Wick rotation (the signed-metric / Wick op = a Class-L signed-Laplacian variant, CLAUDE.md §1).
propagate(L, u0, z) -> Vec — harvest = e^{-zL}·u0 in the eigenbasis (n ≤ 256; the sparse lift is a later rc). ONE eigensolve (symmetric_eigendecompose real / hermitian_eigendecompose complex) → project c = V^H·u0 → per-mode scale c_k·e^{-z·λ_k} → recombine V·(scaled c). The per-mode scalar e^{-zλ_k} = e^{-Re(z)·λ_k}·(cos(Im(z)·λ_k) − i·sin(Im(z)·λ_k)) uses the Class-N exp_series_truncate (real damping, power-of-two-reduced + squared-back so a strongly-damped mode e^{-44} ≈ 7.6e-20 never blows up) + cos_series_truncate / sin_series_truncate (oscillation). z is the complex time (arg(z) the coherence dial; build the partial regime as z = t·(cos φ + i·sin φ)).
The MANDATORY 2π SEAM-FOLD (the correctness crux). The raw Class-N trig series BLOW UP past a convergence radius — cos_series_truncate(44, 1, 18) returns ~2.3e17, not the true cos(44) ≈ 0.9998. Before the series, propagate argument-reduces (seam-folds) the oscillation argument Im(z)·λ_k modulo 2π — the beat seam — using the exact Machin-2π (2π = 32·atan(1/5) − 8·atan(1/239) via atan_series_truncate, quantised to a fixed denominator by exact-integer rounding, no float): the winding w = round(θ/2π) is stripped in exact rational arithmetic, leaving |θ − w·2π| ≤ π where the bounded series is exact. This restores exactness at ANY t·λ — verified: at t·λ = 44 the folded propagate matches the true e^{-zL} to < 5e-15 (norm conserved to 1.0), while the un-folded raw series is 2.3e17. (The fold discards the winding w — folds to one seam side, the epicycle harvest; carrying w to expose the metacycle harvest is a separate rc, #1276.)
eph_harvest(L, u0, z) -> dict — the EPH cascade READ: excite (seed u0 — Class-M grounding, content-neutral) → propagate → the Born-rule harvest |harvest_i|² per node (the reaction-center energy; energy = relevance) → rank the nodes. Returns ranked_nodes (energy-descending), per-node energies (Class-K re² + im², no abs()), the reaction_center (top-ranked), the total_energy (= the coherence budget — conserved in the coherent limit, damped below it in the thermal limit, the monotonic Wick dial), and the raw complex harvest. Composes propagate (c_dispatched) + the Born magnitude + rank — no new C symbol.
SAME-RC C PEER (srmech_eph_propagate.c). NEW exported srmech_eph_propagate + srmech_eph_propagate_arena_bytes — a Class-L composite over srmech_hermitian_eigendecompose_ws + srmech_exp + srmech_cos + srmech_sin (their internal Q61 octant reduction IS the 2π argument fold in the fixed-point basis — the algebraic twin of the Python op's explicit Machin-2π Class-N-series seam-fold, so both stay correct at any t·λ). Caller-arena (all scratch bump-carved from ws), malloc-free, no abs, pedantic -Werror / /WX clean in BOTH -DNDEBUG and asserts-live. Python == C is value-parity: the harvest is basis-invariant (each eigenvector appears in both V and V^H), so it agrees regardless of the eigenvector sign / degenerate-subspace basis convention — measured ≤ 3.4e-14 (native-vs-pure over random real + complex-Hermitian L, every dial). ABI stays 3 (new symbols do not bump ABI).
[0.9.0rc135] - 2026-07-04¶
DEMAND-LOADED gene expression — bounded RAM WITHOUT bounded availability, via two NEW gene_express_plan / genome_genes_expressed ops (UPSTREAM §134, #1273 — siona green-light; the #736 probe made shippable; +2 ops; NO genome-format bump — v11 stays). The #736 probe PROVED (measured, byte-identical): a genome can be demand-loaded — decide WHICH genes express by reading only the regulatory caps, then page in ONLY the expressed byte-ranges. rc135 ships the two ops. tools.total 399 → 401 (the two NEW ToolEntries). ABI stays 3 (one NEW exported symbol — additive). NO genome-format bump — the ops READ the existing rc115 v4 manifest offsets + the delivered E1/E2/E4/E3 inline gates (add no marker / no block KIND), so the format stays v11 (asserted in both ops). numpy stays absent; no abs() — exact Class-I bitwise. 5 SSOT files rc134 → rc135.
gene_express_plan(strand_or_path, the_one, cell_state) -> [(label, byte_offset, byte_len), …] — the offset-only LOAD-PLAN: computes the EXPRESSED set + each expressed unit's ON-DISK byte-range WITHOUT reading content (never decodes a leaf). Two variants, dispatched on the input. PATH variant (variant b — the PRIMARY demand-load case): given a genome DIRECTORY, for each chromosome REGION seek to the manifest byte_offset and read ONLY the head GATE cap (the second block, one leaf_dim-byte cap right after the CHROM cap), evaluate its inline gate (E1 0x67 / E2 0x62 / E4 0x77 / E3 0x64 — the delivered gates) against cell_state, and include the EXPRESSED regions' (chromosome_label, byte_offset, byte_len). It MUST NOT read the region body — bounded RAM AND bounded I/O (the plan touches only one gate cap per chromosome; measured n_chrom·leaf_dim bytes ≪ the full body). STRAND variant (variant a — the in-memory fallback): skeleton-scan an in-memory strand (read caps + one marker byte/turn, SEEK PAST each data-turn payload), splitting on the inline GENE caps and delimiting each EXPRESSED gene's on-disk byte-range; its expressed-label set equals gene_express's.
genome_genes_expressed(path, the_one, cell_state) -> [(label, leaves), …] — the PARTIAL-LOAD reader: uses the PATH plan to SEEK + load + decode ONLY the EXPRESSED chromosome regions, filters each region's genes by gene_express, and returns BYTE-IDENTICAL to the expressed subset of a full gene_express over the whole genome, WITHOUT loading the unexpressed regions.
The SIONA TWO-GENOME LAYOUT — validated. srmech supports siona's community=chromosome design: an ORGANELLE chromosome (an E2 always-on service gate — expresses for ANY cell_state) + NUCLEAR chromosomes (E1/E2/E4/E3 single-community gates) → the plan ALWAYS includes the organelle chromosome + ONLY the cell_state-matching nuclear chromosome. The per-chromosome head gate IS the community gate, so an unexpressed community (head gate off) contributes no expressed gene and its region is never paged in. Mixed E1/E2/E4/E3 gate-types across chromosomes in ONE genome are the delivered gates — the plan gates by the inline mask regardless of kind (the graded E3 head routes through the level op, matching Python _gene_expresses).
SAME-RC C PEER (srmech_genome.c). NEW exported srmech_genome_gene_express_plan — reads the manifest, seeks each region's head gate cap (bounded I/O; never a region body), evaluates the gate via srmech_genome_gene_express (E1/E2/E4) or srmech_genome_gene_express_levels (E3 graded), and emits the EXPRESSED regions in a canonical big-endian [u32 n] + per-record [u32 label_len][label][u64 offset][u64 len] serialization. Python==C byte-identical on the plan offsets (native-vs-forced-pure, every cell_state) AND the partial-load leaves; the reader composes the C plan + gene_express decode. Caller-arena (the manifest parses in ws; the gate cap is a fixed stack buffer); malloc-free; no abs; a READ; pedantic -Werror//WX clean in BOTH -DNDEBUG and asserts-live. ABI stays 3 (a new symbol does not bump ABI).
MPM CITATION FIX (this rc). gene_express's cell-type-SELECTION facet was cited to Alberts NBK26887 ("From DNA to RNA" — the transcription-MECHANISM chapter); corrected to NBK26872 ("How Genetic Switches Work" — MPM-verified first-hand OA: "Different selections of gene regulatory proteins are present in different cell types and thereby direct the patterns of gene expression that give each cell type its unique characteristics"). NBK26887 is the transcription facet, not the cell-type-selection facet. Fixed in genome.gene_express docstring + its tool_schema summary; the two attestation-pin tests re-pinned to NBK26872.
ATTESTED BIOLOGY (ONE FACET — #728 discipline). Demand-loaded differential gene expression — the expressed subset materialised on demand, the unexpressed genes never paged in — as the on-demand-transcription / cell-type-selection facet (Alberts et al., Molecular Biology of the Cell 4th ed., "How Genetic Switches Work", NCBI NBK26872). Framed as CONTEXT, NOT a map srmech reproduces.
[0.9.0rc134] - 2026-07-04¶
MODULATOR-CONSTRAINT (M3) — the COMPLETE inverse of gene_express, the LAST rung of the E-M ladder, via two NEW modulator_constraint / modulator_constraint_satisfies ops (UPSTREAM §133, #733; +2 ops; NO genome-format bump — v11 stays). M1 (modulator_recover, rc133) gave the SOUND two-sided FLOOR from the EXPRESSED genes; M2 (modulator_consistent) forward-CHECKS one candidate. M3 returns the EXACT CONSTRAINT characterizing the WHOLE set of cell-states consistent with an observed expression — a COMPACT structured constraint, NEVER an enumeration (the consistent set can be exponential). It ADDS what M1 left out: (a) the DISJUNCTIVE clauses from UN-expressed genes (an un-expressed E1 gene proves (cs & a) != a OR (cs & r) != 0 = a nand clause; an un-expressed E2 gene ANDs one nand per DNF term); (b) the FULL expressed-E2 disjunction (M1 only took the sound intersection-over-clauses; M3 adds the exact or_terms — an expressed label with ≥ 2 boolean terms expresses iff ≥ 1 term fully matches); © the general-gate inverse (an EXPRESSED E4 threshold → Σ wᵢ·bit_i(cs) ≥ θ, UN-expressed → Σ < θ; an E3 graded EXPRESSED → Σ ≥ 1, UN-expressed → Σ ≤ 0) — CONSTRAINT-SATISFACTION, not a mask-OR. tools.total 397 → 399 (the two NEW ToolEntries). ABI stays 3 (two NEW exported symbols — additive). NO genome-format bump — a READ over the existing gene caps, so the format stays v11. numpy stays absent; no abs() — exact Class-I bitwise, Class-N integer sums, the inequality SENSE a Class-K sign. 5 SSOT files rc133 → rc134.
M3 — modulator_constraint(strand, the_one, expressed_labels) -> dict. Returns a JSON-native compact constraint: certain_on / certain_off (M1's floor pins); clauses (a list of DISJUNCTIVE bit-clauses — {"kind":"nand","any_absent":mask,"any_present":mask} from un-expressed E1/E2 + {"kind":"or_terms","terms":[{"present":a,"absent":r},…]} from the expressed-boolean disjunction); inequalities (E4 as {"weights":[…],"threshold":θ,"sense":">="|"<"}); levels (E3 as {"weights":[…],"denom":D,"positive":bool}); satisfiable (bool); free_bits / solution_note (an HONEST size characterization — the count of referenced-but-unpinned bits + a note that NEVER enumerates the solution set); sound_complete / sound_only_labels (the honest completeness self-report). All clauses / inequalities / levels are ANDed.
SOUND + COMPLETE (the load-bearing DoD, proven by EXHAUSTIVE cross-check). SOUND — every cell_state M2 reports CONSISTENT satisfies the constraint (verified on ≥ 4 chromosomes incl. un-expressed-heavy + full-mix). COMPLETE (satisfies ⟺ CONSISTENT) for the boolean gate-types E1/E2 at ANY label multiplicity AND for a unique single E4/E3 gene — proven by enumerating ALL cell-states and asserting modulator_constraint_satisfies exactly equals modulator_consistent(...)=="CONSISTENT". SOUND-ONLY (an over-approximation, HONESTLY flagged in sound_only_labels with sound_complete=False) for an EXPRESSED label that is a genuine CROSS-TYPE disjunction (a duplicated label spanning boolean AND threshold/graded, or ≥ 2 threshold/graded genes) — that OR has no exact flat-clause form, so its expressed requirement is DROPPED to stay sound. UN-expressed labels are COMPLETE for EVERY gate-type (a conjunction of exact silence constraints). satisfiable is True for a real (came-from-a-state) expression and False for a hand-crafted contradiction (e.g. two genes that can't co-express → a pin-contradiction; a nonexistent expected label) — FALSE is always a PROOF (the sound detectors), and within the free-bit bound it is decided EXACTLY by an INTERNAL bounded search (never a returned enumeration).
M3 checker — modulator_constraint_satisfies(constraint, candidate_cell_state) -> bool. The runnable predicate that makes the sound-AND-complete claim TESTABLE: checks the floor pins + every nand / or_terms clause + every E4 inequality + every E3 level, ALL ANDed.
SAME-RC C PEERS (srmech_genome.c; scope = the BOOLEAN part). NEW exported srmech_genome_modulator_constraint (emits the floor + nand / or_terms clauses into a caller-arena buffer in a canonical big-endian serialization) + srmech_genome_modulator_constraint_satisfies (checks the boolean part of an emitted buffer against a candidate). Python==C byte-identical on the emitted BOOLEAN constraint (over every observed label subset) + the boolean-satisfies verdict. The E4 inequality / E3 level emit + satisfiability are computed Python-side (the owed-C = the general-gate emit, tracked for a follow-up rc). Caller-arena; malloc-free; no abs; a READ; pedantic -Werror//WX clean in BOTH -DNDEBUG and asserts-live. ABI stays 3 (new symbols do not bump ABI).
ATTESTED BIOLOGY (ONE FACET — #728 discipline, NOT a claim srmech reproduces it). The full inverse problem = inferring the COMPLETE regulatory-input constraint from an expression profile — a constraint-satisfaction view of gene-regulatory-network (GRN) inference: Marbach D, Costello JC, Küffner R, et al., "Wisdom of crowds for robust gene network inference", Nature Methods 9(8):796–804 (2012), DOI 10.1038/nmeth.2016 (OA: NIH PMC3512113). Framed explicitly as analog / CONTEXT, NOT as a map srmech implements.
[0.9.0rc133] - 2026-07-04¶
MODULATOR-RECOVERY (M1 + M2) — the INVERSE of gene_express, via two NEW modulator_recover / modulator_consistent ops (UPSTREAM §133, #733; +2 ops; NO genome-format bump — v11 stays). The E-ladder (rc128–132) is the FORWARD map (cell_state → expressed genes). rc133 begins the INVERSE (the #728 candidate-b): GIVEN an OBSERVED expressed set (+ the strand's gene regulatory specs), recover what the cell_state that produced it must look like. It is UNDER-DETERMINED (many cell_states → the SAME expressed subset), so the exact cell_state is IRRECOVERABLE BY CONSTRUCTION — and the only honest form is a ONE-SIDED verdict, the SAME recoverability discipline / op_verdict EQUAL/UNKNOWN contract as op_provenance (rc117) / RecoverableFold / the #725 null: recover the EXACT complement we can PROVE, flag the rest UNKNOWN. tools.total 395 → 397 (the two NEW ToolEntries). ABI stays 3 (two NEW exported symbols — additive; every existing signature unchanged). NO genome-format bump — the ops add no marker / no block KIND, only a READ over the existing gene caps, so the format stays v11. numpy stays absent; no abs() — exact Class-I bitwise. 5 SSOT files rc132 → rc133.
M1 — modulator_recover(strand, the_one, expressed_labels) -> dict. Recovers the TWO-SIDED FLOOR (sharpened by the rc129 activator/repressor two-mask): certain_on = the bits every consistent cell_state MUST have SET — each EXPRESSED E1 gene (klein4-mask 0x67 / plain 0x47) proves its activator bits are on (OR the activator masks); each EXPRESSED E2 gene (boolean DNF 0x62) proves ≥ 1 clause matched, so the bits set in EVERY clause's activator (the INTERSECTION-over-clauses activator) are certain-on. certain_off = the repressor duals (OR of expressed E1 repressor masks + the intersection-over-clauses repressor of expressed E2 genes). E4 threshold (0x77) / E3 graded (0x64) / UN-expressed genes contribute NOTHING to the clean floor (a failed threshold / an absent gene is a DISJUNCTION — no clean single-bit certainty; that is M3's job — do NOT over-claim). undetermined = the referenced condition bits (the union of bits ANY gene reads) minus certain_on ∪ certain_off. verdict = EXACT if the floor covers ALL referenced bits, PARTIAL if some pinned, UNKNOWN if none — mirroring op_verdict's one-sidedness. Returns {"certain_on": int, "certain_off": int, "undetermined": int, "verdict": str} (all JSON-native).
THE SOUNDNESS CONTRACT (the load-bearing DoD). For EVERY cell_state that M2 reports CONSISTENT with expressed_labels, (state & certain_on) == certain_on AND (state & certain_off) == 0. Proven by exhaustive cross-check on small chromosomes (enumerate all cell_states, M2-filter the consistent ones, assert each agrees with M1's floor) over ≥ 3 mixed chromosomes. M1 NEVER over-claims a bit: a gene's floor is applied only when its label is EXPRESSED AND UNIQUE among the strand's gene caps (a duplicated label cannot be attributed — the expressed SET collapses duplicates — so NEITHER contributes).
M2 — modulator_consistent(strand, the_one, expressed_labels, candidate_cell_state) -> str. Forward-check: set(labels of gene_express(strand, the_one, candidate)) == set(expressed_labels) → "CONSISTENT" else "INCONSISTENT". ONE-SIDED: CONSISTENT = "could be the state" (many may be — expression is under-determined), NEVER "it IS the state". Reuses the FORWARD gene_express (no new gate logic — it dispatches on each gene's E1/E2/E4 gate-type exactly as the forward map does), so M1's floor is SOUND precisely because every state M2 calls CONSISTENT satisfies it.
SAME-RC C PEERS (srmech_genome.c). NEW exported srmech_genome_modulator_recover (walks the GENE-CAP subset body → certain_on / certain_off / undetermined / a verdict code) + srmech_genome_modulator_consistent (walks the gene caps, reuses the forward srmech_genome_gene_express per gene, both-subset set equality → CONSISTENT/INCONSISTENT). Caller-arena-free (no scratch — a bitwise fold / a forward re-walk); malloc-free; no abs; NEVER mutates the body; pedantic -Werror//WX clean in BOTH -DNDEBUG and asserts-live. The whole-strand peers take the gene-cap subset (the data turns don't gate expression, so the caller strips them, keeping the walk uniform-width). An int64 threshold / graded OVERFLOW in M2 defers to the exact pure-Python path. ABI stays 3 (new symbols do not bump ABI). Python==C byte-identical on the dict fields + the verdict / consistency string.
ATTESTED BIOLOGY (ONE FACET — the inverse of expression; #728 discipline, NOT a claim srmech reproduces it). Gene-regulatory-network (GRN) inference — reverse-engineering the regulatory state / network from an expression pattern — is a real biological-computational problem, the biology analog of this inverse: Marbach D, Costello JC, Küffner R, et al., "Wisdom of crowds for robust gene network inference", Nature Methods 9(8):796–804 (2012), DOI 10.1038/nmeth.2016 (OA: NIH PMC3512113) — the DREAM5 blind assessment of GRN-inference methods. Framed explicitly as CONTEXT / analog, NOT as a map srmech implements.
[0.9.0rc132] - 2026-07-04¶
GRADED / ANALOG expression LEVEL (E3) — an exact-rational dose-response as an ORTHOGONAL axis on top of the E1/E2/E4 gate-type family, via a NEW gene_express_levels op (UPSTREAM §132, #732; +1 op; genome format v10 → v11). The gate-types (E1 klein4_mask / E2 boolean_dnf / E4 threshold) decide IF a gene expresses (a BINARY switch); E3 adds the orthogonal HOW MUCH axis — a graded / analog LEVEL (real biology: expression is quantitative, not just on/off). tools.total 394 → 395 (the NEW gene_express_levels ToolEntry). ABI stays 3 (a NEW exported symbol srmech_genome_gene_express_levels — additive; every existing signature unchanged). numpy stays absent; no abs() — the clamp is a Class-K sign-branch, the level is an exact Class-N rational, the fraction Class-I gcd-reduced. 5 SSOT files rc131 → rc132.
THE DESIGN DECISION — a NEW op, NOT a breaking change to gene_express. gene_express (which returns the binary expressed SET [(label, leaves)] that rc128–131 + downstream depend on) is UNCHANGED. E3 ADDS gene_express_levels(strand, the_one, cell_state) -> [(label, leaves, (num, den))] returning the expressed genes WITH their exact-rational LEVEL. Both reads coexist and are honestly two distinct reads: gene_express = "which genes are on" (binary set), gene_express_levels = "at what level" (graded). This is full-coverage + non-breaking. (Enriching gene_express to 3-tuples was rejected — it would break the binary-set contract downstream consumers rely on.)
THE LEVEL AXIS IS GENUINELY ORTHOGONAL — it composes with EVERY gate-type. gene_express_levels is uniform over all gene kinds: a binary gene (plain 0x47 / klein4-mask 0x67 / boolean 0x62 / threshold 0x77) is the DEGENERATE {0, 1} graded case — included at LEVEL exact-rational 1 = (1, 1) iff its E1/E2/E4 gate PASSES (the SAME §128/§130/§131 decision gene_express uses), ABSENT otherwise; a graded gene → its exact-rational dose-response. So every gene gene_express returns appears in gene_express_levels at level 1, and vice versa (proven byte-for-byte across all cell_states).
THE MECHANIC — a NEW 0x64 ('d' for dose) CAP VARIANT (v10 → v11 bump). A graded gene carries a per-condition SIGNED integer LEVEL-WEIGHT vector + a POSITIVE integer DENOMINATOR inline; the LEVEL is the reduced exact rational Σᵢ (level_weightᵢ · bit_i(cell_state)) / denom CLAMPED to [0, 1] (a Class-K sign-branch: raw dose ≤ 0 → 0, ≥ denom → 1, else the reduced in-range fraction; SIGNED weights allow an inhibitory input that REDUCES the dose). The denom is the full-expression normalizer (the dose at which the gene is fully ON). The dose-response IS the gate — a graded gene is "expressed" iff its LEVEL > 0, so it also participates in the BINARY gene_express. Exact Class-N rational; Class-I gcd-reduce; Class-K clamp; NO float, NEVER abs().
FORMAT DECISION: v10 → v11 bump (a new marker byte = a new block KIND). Layout [0x64] + label + NUL + gate_type(uint8=3) + n_weights(uint16 BE) + denom(uint64 BE POSITIVE) + n_weights × level_weight(int64 BE SIGNED) + NUL-pad. A new marker keeps 0x77/0x67/0x62/0x47 100% untouched (byte- and code-path), exactly as rc127 (0x74, v6→v7), rc128 (0x67, v7→v8), rc130 (0x62, v8→v9) and rc131 (0x77, v9→v10) bumped. The strand-walk read path is version-INDEPENDENT, so every pre-rc132 genome (plain / rc128 / rc129 / rc130 / rc131) reads + behaves identically in BOTH ops (proven byte- + behaviour-identical across all cell_states). The genome_persistence/v11 + genome_chromosome/v11 rule-preimages track the bump.
THE DEMONSTRATIONS (DoD). The graded level exact-rational — weights=[1,1,1,1], denom=4, popcount-2 cell_state → ½ (reduced); the dose-response sweeps 0 → ¼ → ½ → ¾ → 1 as conditions turn on; SIGNED (inhibitory) weights clamp a non-positive dose to level 0, an overshoot dose to level 1; binary genes → level 1 (and present in gene_express); the gate composes (level 0 = absent, the rational when the dose passes); the theorem (cell_state modulates the LEVEL — same DNA, different cell_state → different LEVELS); the read-time filter (strand byte-identical after gene_express_levels); the bare strand SELF-DESCRIBES its level-weights + denom (_graded_gene_spec, no manifest); a v11 genome saves + pages back + reports the rational level from disk.
THE CHROMOSOME-BUILDER API. chromosome(genes=…) now accepts a 3-tuple with a DICT third element (gene_label, gene_leaves, {"gate": "graded", "weights": [w0, w1, …], "denom": D}) for a graded gene, alongside the existing threshold dict (E4), boolean dict (E2), 4-tuple (klein4 two-mask, E1), 3-tuple-with-int (klein4 activator-only), and 2-tuple (unregulated) forms. Mixing arities/forms is additive + back-compatible.
SAME-RC C PEER (srmech_genome.c). NEW exported srmech_genome_gene_express_levels (per-cap → the reduced exact-rational (num_out, den_out)): a GRADED gene → the clamped reduced dose-response (SIGNED int64 weight decode via genome_read_i64_be; POSITIVE uint64 denom via genome_read_u64_be; exact int64 dose accumulate with an OVERFLOW-not-wrap guard → SRMECH_ERR_OVERFLOW deferring to the exact pure-Python bignum path; Class-K clamp; Class-I reduce via srmech_gcd); a BINARY gene reuses srmech_genome_gene_express and returns (1, 1)/(0, 1). Caller-arena-free (a per-cap decision); malloc-free; no abs; NEVER mutates cap; pedantic -Werror//WX clean in BOTH -DNDEBUG and asserts-live. The block walkers + scanners gain the 0x64 case (excluded from the data-turn count like the 0x67/0x62/0x77 genes). ABI stays 3 (a new symbol does not bump ABI). Python==C byte-identical on the per-gene level (7000+ randomized graded + binary cases, incl. SIGNED weights, both clamp branches, and the reduced fractions).
BIOLOGY ATTESTED AS ONE FACET (NOT a reduction — genes have other regulation too). Graded / quantitative transcriptional output — the amount of transcription is tuned by the bound regulators: Alberts, Johnson, Lewis, Raff, Roberts & Walter, Molecular Biology of the Cell 4th ed. (Garland Science, 2002), "How Genetic Switches Work" → "Gene Activator Proteins Work Synergistically", NCBI Bookshelf NBK26872 (OA-verified first-hand from the NCBI page): the joint effect of several activators on the transcription RATE is "not merely the sum … but the product" — a graded, analog modulation of the expression LEVEL, not a binary switch (exactly the quantitative dose-response E3 models).
[0.9.0rc131] - 2026-07-04¶
THRESHOLD REGULATORY GATE-TYPE (E4) — a linear-threshold (perceptron) gate Σᵢ weightᵢ·bit_i(cell_state) ≥ threshold as a THIRD gate-type in the E1(klein4_mask)/E2(boolean_dnf) dispatch family (UPSTREAM §131, #731; +0 ops; genome format v9 → v10). tools.total stays 394 (gene_express + chromosome are ENRICHED, not new ops; no new ToolEntry). ABI stays 3 (the srmech_genome_gene_express signature is UNCHANGED — only its decode + decision logic gain the threshold branch; the new genome_threshold_expresses + genome_read_i64_be are static internal helpers, not exported symbols); numpy stays absent; no abs() — the decision is the SIGN of (Σ − threshold), a Class-K sign-branch (abs-ing the sum would discard the inhibitory sign). 5 SSOT files rc130 → rc131.
WHY E4 IS GENUINELY DISTINCT FROM E2 (not redundant). E2's DNF is functionally complete, so it can represent any boolean function — but a linear-threshold function (e.g. MAJORITY-of-n, or a weighted morphogen dose-sum) requires an EXPONENTIALLY-large DNF (the minimal DNF for MAJORITY-of-n has C(n, ⌈n/2⌉) terms). E4 captures COMPACTLY what E2 cannot: linear-threshold functions ⊄ small-DNF. E4 is the "integrate many weighted inputs / morphogen-gradient threshold / additive cis-regulatory enhancer" model — orthogonal to E2's per-clause combinatorial logic, not a re-skin of it.
THE MECHANIC — a NEW 0x77 ('w' for weighted) CAP VARIANT (v9 → v10 bump). A threshold gene carries a per-condition SIGNED integer WEIGHT vector + an integer THRESHOLD inline; the rule is Σᵢ (weightᵢ · bit_i(cell_state)) ≥ threshold → express (weight i gates condition bit i). SIGNED weights are allowed — an inhibitory input (a repressive TF) is a NEGATIVE weight (real biology). The sum is an exact integer; the decision is the SIGN of (Σ − threshold) (Class-K pin-slot, NEVER abs()), with an INCLUSIVE boundary (Σ == threshold EXPRESSES; Σ == threshold − 1 does NOT). Exact Class-I/N integer arithmetic; NO float.
FORMAT DECISION: v9 → v10 bump (a new marker byte = a new block KIND). Layout [0x77] + label + NUL + gate_type(uint8=2) + n_weights(uint16 BE) + threshold(int64 BE SIGNED) + n_weights × weight(int64 BE SIGNED) + NUL-pad. A new marker keeps 0x67/0x62 100% untouched (byte- and code-path), exactly as rc127 (0x74, v6→v7), rc128 (0x67, v7→v8) and rc130 (0x62, v8→v9) bumped. The strand-walk read path is version-INDEPENDENT, so every pre-rc131 genome (plain / rc128 / rc129 / rc130) reads + behaves identically (proven byte- + behaviour-identical). The genome_persistence/v10 + genome_chromosome/v10 rule-preimages track the bump.
THE DEMONSTRATIONS (DoD). Threshold rule exact incl. SIGNED (inhibitory) weights + the boundary (Σ==θ expresses, Σ==θ−1 doesn't); a MAJORITY gate (weights all 1, θ = ⌈n/2⌉) — the E2-can't-do-compactly case — demonstrated; the family dispatch (E1/E2/E4 coexist in one chromosome, back-compat); the op⊗operand theorem (same DNA, different cell_state → different expressed subset); the read-time filter (strand byte-identical after gene_express); the bare strand SELF-DESCRIBES its weights + threshold (_threshold_gene_spec, no manifest); a v10 genome saves + pages back + rebuilds-by-scan.
THE CHROMOSOME-BUILDER API. chromosome(genes=…) now accepts a 3-tuple with a DICT third element (gene_label, gene_leaves, {"gate": "threshold", "weights": [w0, w1, …], "threshold": θ}) for a threshold gene, alongside the existing boolean dict (E2), 4-tuple (klein4 two-mask, E1), 3-tuple-with-int (klein4 activator-only), and 2-tuple (unregulated) forms. Mixing arities/forms is additive + back-compatible.
SAME-RC C PEER (srmech_genome.c). srmech_genome_gene_express gains the 0x77 branch → the new static genome_threshold_expresses (variable-length SIGNED-weight decode via genome_read_i64_be — a PORTABLE two's-complement reader; exact int64 accumulate; sign-compare total >= threshold compared DIRECTLY so it cannot overflow; on an int64-accumulate overflow it returns SRMECH_ERR_OVERFLOW so the caller falls to the exact pure-Python bignum path; caller-arena-free — a per-cap decision; malloc-free; no abs; NEVER mutates cap; pedantic -Werror//WX clean in BOTH -DNDEBUG and asserts-live). The block walker + scanners (genome_block_len / genome_fill_strings / append-walk) gain the 0x77 case (excluded from the data-turn count like the 0x67/0x62 genes). ABI stays 3 (signature unchanged; new static helpers only). Python==C byte-identical on the per-gene threshold decision incl. SIGNED weights + the Σ==θ / Σ==θ−1 boundary + a MAJORITY gate + a grid.
BIOLOGY ATTESTED AS ONE FACET (NOT a reduction — genes have other regulation too). Additive / threshold enhancer integration — the morphogen-gradient threshold model: Alberts, Johnson, Lewis, Raff, Roberts & Walter, Molecular Biology of the Cell 4th ed. (Garland Science, 2002), "Drosophila and the Molecular Genetics of Pattern Formation: Genesis of the Body Plan", NCBI Bookshelf NBK26906 (OA-verified first-hand from the NCBI page): the Dorsal protein "turns on or off the expression of different sets of genes depending on its concentration" — "Most ventrally—where the concentration of Dorsal protein is highest—it switches on … twist …; Most dorsally, where the concentration of Dorsal protein is lowest, the cells switch on decapentaplegic" — i.e. a graded morphogen crossing distinct THRESHOLD concentrations sets which genes express (exactly the weighted-dose-sum ≥ threshold integration E4 models).
[0.9.0rc130] - 2026-07-04¶
BOOLEAN REGULATORY GATE-TYPE (E2) — arbitrary boolean logic (AND/OR/NOT/XOR) over the condition bits as a GATE-TYPE in a dispatch family, with rc129's Klein-4 activator/repressor mask kept as the fast common case (UPSTREAM §130, #730; +0 ops; genome format v8 → v9). tools.total stays 394 (gene_express + chromosome are ENRICHED, not new ops; no new ToolEntry). ABI stays 3 (the srmech_genome_gene_express signature is UNCHANGED — only its decode + decision logic gain the boolean branch; the new genome_dnf_expresses is a static internal helper, not an exported symbol); numpy stays absent; no abs() (the masks / cell_state are exact Class-I bitwise integers, never negated). 5 SSOT files rc129 → rc130.
WHAT rc128/rc129 LACKED. rc128 gave gene_express an all-activator AND-mask; rc129 (E1) enriched it to a Klein-4 activator/repressor two-mask — but that is still ONE conjunctive clause. Genuinely combinatorial cis-regulatory logic integrates multiple TFs by AND/OR/NOT/XOR enhancer logic (Alberts et al., Molecular Biology of the Cell 4th ed., "How Genetic Switches Work"). E2 adds the GENERAL case: an arbitrary boolean function over the condition bits.
THE GATE-TYPE DISPATCH FAMILY (E1 ⊂ E2, E1 stays the fast path — NOT a replacement). A regulatory gene declares a gate_type; gene_express dispatches on it (the gate_type is the cap marker, and for a boolean gene is ALSO stored as an explicit byte so the bare strand self-describes it):
* gate_type = klein4_mask (E1/rc129, the DEFAULT / FAST common case) → the existing (cs & act) == act AND (cs & rep) == 0 rule, carried in a plain GENE cap (0x47) or Klein-4-mask regulatory gene cap (0x67). UNCHANGED — stays the compact fast path.
* gate_type = boolean (E2, NEW) → an arbitrary boolean formula over the condition bits, carried in a new BOOLEAN GENE cap (0x62). The general escape hatch.
THE BOOLEAN ENCODING — DNF (judged the cleanest; sum-of-products). The 0x62 cap carries a disjunctive normal form: a list of (require_present_mask, require_absent_mask) AND-clauses; the gene expresses iff ANY clause matches ((cs & act) == act AND (cs & rep) == 0). Chosen over an option-(a) truth-table because it is the NATURAL generalisation of E1 — E1's activator/repressor two-mask IS exactly a 1-CLAUSE DNF, so E1 ⊂ E2 literally (the family is conceptually clean, not a bolt-on) — and it stays exact bitwise (Class-I), compact, and self-describing. DNF is functionally complete: AND = 1-clause [(a|b, 0)] (= E1 recovered), OR = 2-clause [(a,0),(b,0)], NOT = [(0,a)] (repressor), XOR = 2-clause [(a,b),(b,a)]. The empty DNF (0 clauses) is the OR-identity FALSE = never expresses.
THE MECHANIC — A NEW 0x62 CAP VARIANT (v8 → v9 bump), NOT an overload of 0x67. Layout [0x62] + label + NUL + gate_type(uint8) + n_terms(uint16 BE) + n_terms × (activator(uint64 BE) + repressor(uint64 BE)) + NUL-pad. Judged a NEW cap variant (not a 0x67 extension) because the rc129 two-mask reader reads the activator+repressor at fixed offsets with no room for a byte-clean discriminator — so a new marker keeps 0x67 the fast path 100% untouched and makes E1 genuinely the compact special case. FORMAT DECISION: v8 → v9 bump — a new marker byte is a new block KIND, exactly as rc127 (0x74, v6→v7) and rc128 (0x67, v7→v8) bumped; the strand-walk read path is version-INDEPENDENT, so every pre-rc130 genome reads identically (proven). The genome_persistence/v9 + genome_chromosome/v9 rule-preimages track the bump.
BACK-COMPAT (PROVEN byte- + behaviour-identical). Plain genes (0x47), rc128 single-mask genes and rc129 two-mask genes (0x67) are byte-identical (their markers/layout are untouched — only a NEW 0x62 marker is added) and behave identically (the klein4_mask dispatch path is unchanged). The E1 lac operon reproduced via a 1-clause DNF cross-checks IDENTICAL to the rc129 0x67 gene. gene_express stays a READ — it NEVER mutates the strand (the input is byte-identical after). The bare strand SELF-DESCRIBES the gate_type + DNF by _boolean_gene_dnf (no manifest).
THE CHROMOSOME-BUILDER API. chromosome(genes=…) now accepts a 3-tuple with a DICT third element (gene_label, gene_leaves, {"gate": "boolean", "dnf": [(act, rep), …]}) for a boolean gene, alongside the existing 4-tuple (klein4 two-mask), 3-tuple-with-int (klein4 activator-only), and 2-tuple (unregulated) forms. Mixing arities/forms is additive + back-compatible.
SAME-RC C PEER (srmech_genome.c). srmech_genome_gene_express gains the 0x62 branch → the new static genome_dnf_expresses (variable-length DNF term-list decode + evaluate; caller-arena-free — a per-cap decision; malloc-free; no abs; NEVER mutates cap; pedantic -Werror//WX clean in BOTH -DNDEBUG and asserts-live). The block walker + scanners (genome_block_len / genome_fill_strings / append-walk) gain the 0x62 case (excluded from the data-turn count like the 0x67 gene). Python==C byte-identical on the per-gene DNF decision across AND/OR/NOT/XOR + the E1-as-1-clause lac operon + a grid.
BIOLOGY ATTESTED AS ONE FACET (NOT a reduction — genes have other regulation too). Combinatorial gene control / multi-input cis-regulatory logic — Alberts, Johnson, Lewis, Raff, Roberts & Walter, Molecular Biology of the Cell 4th ed. (Garland Science, 2002), "How Genetic Switches Work", NCBI Bookshelf NBK26872 (OA-verified first-hand from the NCBI page): the Drosophila eve gene is regulated by combinatorial controls — "Seven combinations of gene regulatory proteins—one combination for each stripe—activate eve expression, while many other combinations … keep the stripe elements silent" — i.e. a COMBINATION of regulators, not a single one, determines expression (exactly the AND/OR/NOT boolean integration E2 models). The activator/repressor operon exemplar stays Jacob & Monod (1961), J Mol Biol 3:318-356 (the E1 klein4_mask fast path).
[0.9.0rc129] - 2026-07-04¶
KLEIN-4 REGULATORY ROLES — enrich rc128's gene_express with activator/repressor logic, each regulatory condition a KLEIN-4 SECTOR (the genome's native alphabet) (UPSTREAM §129, #729; +0 ops; genome format stays v8). tools.total stays 394 (gene_express is ENRICHED, not a new op; no new ToolEntry). ABI stays 3 (the srmech_genome_gene_express signature is unchanged — only its decode + decision logic gain the second mask plane); numpy stays absent; no abs() (the masks / cell_state are exact Class-I bitwise integers, never negated). 5 SSOT files rc128 → rc129.
WHAT rc128 LACKED. rc128 shipped gene_express(strand, the_one, cell_state) with the rule (cell_state & gene_mask) == gene_mask — a pure conjunctive AND-gate = all-ACTIVATOR (all required conditions present). Biology (the lac operon) also has REPRESSORS (require-ABSENT). rc129 enriches the rule to activator/repressor PER CONDITION.
THE KLEIN-4 FRAMING (framework-native, not an extra mask). Each regulatory CONDITION (bit position) carries one of FOUR roles — the genome's NATIVE Klein-4 alphabet (element_type 0 = klein4, the 2-bit {0,1,2,3} symbol). The per-condition pair (act_bit, rep_bit) IS the Klein-4 sector (the two bit-planes are the two Z2 factors of V = Z2 × Z2): (0,0) don't-care / (1,0) activator (require-present) / (0,1) repressor (require-absent) / (1,1) never (present AND absent = contradiction → the gene is auto-silenced). ENCODING = two parallel bitmasks (activator_mask, repressor_mask), the two Klein-4 bit-planes over the 64 conditions.
THE RULE (exact Class-I bitwise). A gene expresses iff (cell_state & activator_mask) == activator_mask (ALL activators present) AND (cell_state & repressor_mask) == 0 (NO repressor present). A 'never' bit auto-silences: (cs & act) == act needs it set while (cs & rep) == 0 needs it clear → contradiction. NO float, NO abs(). THE LAC-OPERON EXEMPLAR (Jacob & Monod 1961): a gene with activator = lactose-bit, repressor = glucose-bit expresses iff lactose PRESENT and glucose ABSENT.
THE MECHANIC — SAME 0x67 CAP, DUAL-READ (no new marker, no format bump). A regulatory gene now carries TWO consecutive uint64 mask fields inline — layout [0x67] + label + NUL + activator(uint64 BE) [+ repressor(uint64 BE)] + NUL-pad. The repressor plane occupies the 8 bytes that were NUL PADDING in a rc128 single-mask cap, so the second Klein-4 bit-plane was latent in the padding all along. DUAL-READ: the reader reads the activator (always present) + the repressor (the next 8 bytes if the leaf has room, else 0), so a rc128 single-mask cap reads as activator = mask, repressor = 0 (a pure all-activator AND-gate — IDENTICAL rc128 behaviour). BYTE-COMPAT: the writer emits the rc128 8-byte (activator-only) form when repressor == 0 (the 0 repressor IS the padding), so an activator-only rc129 gene is byte-identical to a rc128 gene; it spends the second 8-byte field only when repressor != 0. FORMAT DECISION: NO v8→v9 bump — this is an ADDITIVE extension of an EXISTING block kind (marker 0x67), NOT a new marker; every prior format bump tracked a new block KIND. rc128 single-mask genes + plain genes + v2 fixtures read UNCHANGED; the genome_persistence/v8 + genome_chromosome/v8 rule-preimage hashes stay v8, so all existing genome manifests stay byte-identical.
THE CHROMOSOME-BUILDER API. chromosome(genes=…) now accepts a 4-tuple (gene_label, gene_leaves, activator_mask, repressor_mask) for a two-plane regulatory gene, alongside the 3-tuple (gene_label, gene_leaves, activator_mask) (§128 activator-only, repressor 0, byte-identical to rc128) and the 2-tuple (gene_label, gene_leaves) (unregulated / always-expressed). Mixing arities is additive + back-compatible.
READ-TIME FILTER (unchanged invariant). gene_express is still a READ — it NEVER mutates the strand (biology does not rewrite DNA to regulate it); the strand is byte-identical after. The bare strand SELF-DESCRIBES BOTH masks by _regulatory_gene_masks (a (activator, repressor) bare-strand read, no manifest).
SAME-RC C PEER (srmech_genome.c). srmech_genome_gene_express gains the two-plane decode (activator always; repressor iff the leaf has room, else 0) + the two-condition rule, byte-identically to the pure Python decision (no arena — a per-cap decision, malloc-free, JPL-clean, no abs, NEVER mutates cap; the signature is unchanged → ABI stays 3, mask_out now reports the ACTIVATOR plane). Pedantic (-Werror//WX) clean in BOTH -DNDEBUG and asserts-live. Python==C byte-identical on the per-gene decision across the 4 Klein-4 roles + the lac operon + a (activator, repressor, cell_state) grid.
BIOLOGY ATTESTED AS ONE FACET (NOT a reduction — genes have other regulation too). The activator/repressor (operon) model — Jacob F & Monod J (1961), "Genetic regulatory mechanisms in the synthesis of proteins", Journal of Molecular Biology 3(3):318-356 (doi:10.1016/S0022-2836(61)80072-7) — the classic lac-operon repressor model (OA-verified). Differential gene expression stays attested to Alberts et al., Molecular Biology of the Cell 4th ed., NCBI Bookshelf NBK26887.
[0.9.0rc128] - 2026-07-04¶
CELL-STATE-MODULATED GENE EXPRESSION — a READ-TIME FILTER where the cell_state OPERAND modulates which genes express, lifting the rc127 op⊗operand THEOREM one scale up (UPSTREAM §128, #728; +1 op; genome format v7 → v8). tools.total 393 → 394 (+1: genome.gene_express c_dispatched). ABI stays 3 (additive cap kind + one new symbol — no existing function signature changed); numpy stays absent; no abs() (the mask / cell_state is an exact Class-I bitwise integer, never negated). 5 SSOT files rc127 → rc128.
THE #728 FINDING IT ANSWERS. The #728 RNA/DNA↔cell probe found the genuine NEXT RUNG above rc127: rc127's active telomere gates ONE divide/senesce BINARY by a carried COUNT; biological gene-regulation gates a SELECTION over MANY genes by the CELL-STATE. This is the SAME op⊗operand theorem (operand-modulates-operator) at the GENOME/expression scale, completing a 3-SCALE FRACTAL TOWER: ribozyme(molecule) ⊂ active-telomere(chromosome, rc127) ⊂ cell-state-expression(genome, THIS).
THE op⊗operand DUALITY, MADE STRUCTURAL (not decorative). Genes carry an inline regulatory MASK (the operand-side structure = the "regulatory region / promoter") in a REGULATORY GENE cap. gene_express (the OPERATOR) is MODULATED by the applied cell_state (the OPERAND): same chromosome, different cell_state → different expressed gene subset. That inequality IS the theorem (parallel to rc127's count-modulates-divide). Same (operand, op) pattern as op_provenance.carry(value, operation) and coupling.RecoverableFold(lossy_bundle, exact_seed_R) — the proven op-carrying carrier (the #726 byte-identical theorem) + the active telomere (rc127) — now with a CELL-STATE operand + an EXPRESSION operator. Verified STRUCTURAL: the expressed subset genuinely changes with cell_state, and the strand is truly unmutated (a READ).
THE MECHANIC. A REGULATORY GENE is opened by a NEW intra-chromosome cap (marker REGULATORY_GENE_MARKER = 0x67 'g', distinct from CHROM 0x43 / GENE 0x47 / v5 KERNEL 0x4B / PACKED 0x51 / KERNEL-telomere 0x6B / ACTIVE-telomere 0x74) carrying an exact non-negative uint64 MASK INLINE — layout [0x67] + utf-8 label + NUL + mask(uint64 big-endian) + NUL-pad, the SAME field shape as the rc127 active telomere with the mask replacing the count (option: extend the gene cap → JUDGED a NEW cap variant, exactly paralleling rc127's active-telomere = telomere + count). Placing the mask AFTER the label's NUL keeps the label decode UNIFORM (_unpack_cap reads it — no regulatory-gene special-case). Build one via a 3-tuple chromosome(the_one=one, genes=[(label, leaves, mask), …]); a 2-tuple (label, leaves) is an UNREGULATED gene (additive). gene_express(strand, the_one, cell_state) -> list is the READ-TIME FILTER: it walks the genes and includes each gene IFF (cell_state & gene_mask) == gene_mask (the cell-state has ALL of the gene's required regulatory conditions present), returning the EXPRESSED subset [(gene_label, gene_leaves), …] (the genes() shape, filtered; leaves uncoupled through the_one).
THE READ-TIME-FILTER FAITHFULNESS — NEVER MUTATES THE STRAND. Biology does NOT rewrite DNA to regulate it; expression is a READ of the regulatory region. gene_express returns a filtered subset built from re-derived leaves and leaves the input strand BYTE-IDENTICAL (asserted before/after). A PLAIN gene (0x47, no mask) is UNREGULATED = mask 0 = ALWAYS EXPRESSED ((cell_state & 0) == 0 for every cell_state) — so old / plain-gene chromosomes always fully express (back-compat).
FORMAT v7 → v8, DUAL-READ (never break an existing genome). Unlike the v6/v7 CHROMOSOME-boundary caps, 0x67 is an INTRA-chromosome gene delimiter (a gene-analog of the plain GENE cap). The block walker (_walk_region_blocks / C genome_block_len), the scanners (genes / partition / _rebuild_manifest_from_body / C genome_scan_chroms / genome_scan_region), _hv_from_block / _block_is_cap, genome_genes and the genome_load streaming path all gain ONE branch — recognise 0x67 as a gene-start cap (excluded from the data-turn count like the plain GENE cap). A plain-gene (no 0x67) genome saved by the v8 writer is byte-identical to v7 EXCEPT the manifest format_version field (the same version-stamp discipline every prior new-block-kind bump used — a v8 writer stamps 8). Proven: a pre-rc128 v2 fixture reads UNCHANGED; the chromosome SELF-DESCRIBES its regulatory masks by bare-strand scan with NO manifest (rebuild-by-scan reproduces the masks, and gene_express filters the rebuilt strand correctly).
SAME-RC C PEER (srmech_genome.c). The walker + scanners gain the 0x67 case; the NEW srmech_genome_gene_express(cap, leaf_dim, cell_state, expressed, mask_out) decodes the regulatory mask and applies the (cell_state & mask) == mask filter (a plain gene → mask 0, always expresses) byte-identically (no arena — a per-cap decision, malloc-free, JPL-clean, no abs, NEVER mutates cap). The v7/v8 rule-preimage strings tracked to v8. Python==C byte-identical — the per-gene decision across a 10-case (mask, cell_state) grid AND genome_save's turns.bin + manifest.json on a regulatory-gene genome (native-vs-forced-pure). Pedantic (-Werror//WX) clean in BOTH -DNDEBUG and asserts-live.
BIOLOGY ATTESTED AS ONE FACET (NOT a reduction — genes have other regulation too). Differential gene expression — Alberts, Johnson, Lewis, Raff, Roberts & Walter, Molecular Biology of the Cell 4th ed. (Garland Science, 2002), chapter "From DNA to RNA", NCBI Bookshelf NBK26887: "a cell can change (or regulate) the expression of each of its genes according to the needs of the moment—most obviously by controlling the production of its RNA" (OA-verified first-hand from the NCBI page).
[0.9.0rc127] - 2026-07-04¶
THE ACTIVE TELOMERE — the Hayflick descending-loop counter that makes the chromosome GENUINELY op⊗operand, turning the #726 lens into a THEOREM (UPSTREAM §127, #726; +2 ops; genome format v6 → v7). tools.total 391 → 393 (+2: genome.active_telomere composition_of_c, genome.telomere_tick c_dispatched). ABI stays 3 (additive cap kind + one new symbol — no existing function signature changed); numpy stays absent; no abs() (the count is an exact Class-I/N integer, never negated). 5 SSOT files rc126 → rc127.
THE #726 FINDING IT ANSWERS. The #726 probe PROVED the genome telomere is a PASSIVE op-SLOT — swapping the cap leaves the leaves unchanged; the_one governs them, telomere-independent — so "chromosome = op⊗operand" was a LENS, not a theorem (it also proved op_provenance(rc117) ≡ RecoverableFold(rc125) byte-identical). The ONE build that makes the chromosome GENUINELY op⊗operand: make the telomere ACTIVE — carry an exact counter (the OPERAND) that MODULATES a downstream op (the OPERATOR).
THE op⊗operand DUALITY, MADE STRUCTURAL. The active telomere is op⊗operand fused in ONE cap: op = the gating rule (telomere_tick's proceed/senesce decision, the field / "how"), operand = the exact count (the excitation / "what"). This is the SAME (operand, op) pattern as op_provenance.carry(value, operation) and coupling.RecoverableFold(lossy_bundle, exact_seed_R) — the proven op-carrying carrier — but with an ACTIVE op (the count changes how the operator works), which is precisely what makes it genuinely op⊗operand vs the passive slot. It carries the DUALITY.md field/excitation duality LOCAL to the chromosome: today a plain chromosome holds the WHAT (leaves) + borrows the HOW (the_one); the active telomere lets it hold BOTH.
THE MECHANIC (a descending Hayflick counter). active_telomere(label, count, dim) is a NEW chromosome-boundary cap (marker ACTIVE_TELOMERE_MARKER = 0x74 't', distinct from CHROM 0x43 / GENE 0x47 / v5 KERNEL 0x4B / PACKED 0x51 / KERNEL-telomere 0x6B) carrying an exact non-negative uint64 COUNT INLINE in the strand — layout [0x74] + utf-8 label + NUL + count(uint64 big-endian) + NUL-pad. Placing the count AFTER the label's NUL keeps the label decode UNIFORM (_unpack_cap reads it — no active-telomere special-case anywhere), so only the count read is active-specific. Build a chromosome with one via chromosome(leaves, the_one, active_count=N). telomere_tick(strand) is the divide/gate: reads the count → count == 0 → honest SENESCENCE (status 'senescent', no daughter — the inform-don't-crash / honest-decline pattern, NEVER a crash) — the Hayflick limit; count > 0 → DECREMENT by exactly 1 (the telomere SHORTENS) + return the DAUGHTER strand (status 'divided'; the same coupled leaves led by an active telomere of count-1 — the telomere GOVERNS the leaves WITHOUT decoding them, like biology shortening the cap not re-synthesising the genes). An active telomere of count N allows EXACTLY N divides, then the N+1-th refuses — same call, operator behaviour SELECTED by the operand.
FORMAT v6 → v7, DUAL-READ (never break an existing genome). The block walker (_walk_region_blocks / C genome_block_len), the scanners (_split_into_chromosomes / _rebuild_manifest_from_body / partition / genes / C genome_count_chroms / genome_scan_chroms / genome_scan_region), _hv_from_block / _block_is_cap and the genome_load streaming path all gain ONE branch — recognise 0x74 as a chromosome-start cap. A plain-telomere (no 0x74) genome saved by the v7 writer is byte-identical to v6 EXCEPT the manifest format_version field (the same version-stamp discipline every prior bump used — a v7 writer stamps 7). Proven: every pre-rc127 genome (v2 fixture through v6 kernels) reads UNCHANGED (the rc114/rc121/rc126 dual-read precedent); the count SELF-DESCRIBES by bare-strand scan with NO manifest.
SAME-RC C PEER (srmech_genome.c). The walker + scanners + append region-scan gain the 0x74 case; the NEW srmech_genome_telomere_tick(cap, leaf_dim, out_cap, senescent, count_after) reads the count (no arena — out_cap is caller-provided, malloc-free, JPL-clean, no abs) and applies the divide/senescence decision byte-identically. The v6/v7 rule-preimage strings tracked to v7. Python==C byte-identical (turns.bin + manifest.json on a genome with an active telomere; the tick's decrement + senescence verdict identical) via native-vs-forced-pure differential (count = 0 / 1 / 5 / 255 / 65537, the full Hayflick countdown). Pedantic (-Werror//WX) clean in BOTH -DNDEBUG and asserts-live.
BIOLOGY ATTESTED AS ONE FACET (NOT a reduction — telomeres also cap/protect). Harley, Futcher & Greider 1990, Nature 345(6274):458 "Telomeres shorten during ageing of human fibroblasts"; Hayflick & Moorhead 1961, Exp Cell Res 25:585 (serial cultivation); Hayflick 1965, Exp Cell Res 37(3):614 (the Hayflick Limit). (The 1961 vol/page 25:585 is kept distinct from the 1965 title at 37:614 — the #726 catch.)
[0.9.0rc126] - 2026-07-04¶
THE UNIFORMLY-KLEIN-4 KERNEL HEADER — the on-disk kernel encoding made 100 % Klein-4, so the O(1) genome_append_kernel FALLS OUT (#1261 / UPSTREAM §89, F1045/F1046; +1 op, non_compute; genome format v5 → v6). tools.total 390 → 391 (+1: genome.genome_append_kernel; the _kernel_telomere cap helper is a private internal, not a tool). ABI stays 3 (manifest-versioned format, additive marker — no function signature changed); numpy stays absent; no abs(). 5 SSOT files rc125 → rc126.
THE ROOT CAUSE. rc121's §60 kernel header (marker 0x4B, a fixed 14-byte byte-TLV prefix — uint64 D + uint32 leaf_dim + uint8 element_type) was a BYTE-TLV block embedded in an otherwise-uniform Klein-4 leaf stream — the SOLE non-Klein-4 residue. Consequences: (1) genome_append (which takes RAW Klein-4 leaves and couples them through the_one via klein4_bind) CANNOT append a kernel WITH its header — unbinding a byte-TLV via klein4_bind fails "must be in {0,1,2,3}"; (2) the interim workaround (append header-LESS + rely on kernel_unpack's back-compat D = leaf_count × leaf_dim trim) only works for leaf_dim-ALIGNED D and produces a MIXED genome.
THE FIX (option A — base-4-encode the header into ONE Klein-4 LEAF). The header's three fields base-4-encode into 52 Klein-4 symbols (_pack_kernel_header_klein4: D → 32 syms uint64, leaf_dim → 16 syms uint32, element_type → 4 syms uint8, Klein-4-zero-padded to leaf_dim; needs leaf_dim >= 52). A v6 kernel chromosome is now [kernel_telomere, coupled_klein4_header, content-turns…] — EVERY data leaf (header included) is 100 % Klein-4, so the whole chromosome rides the plain coupled-turn path and the O(1) genome_append_kernel(path, label, hv, *, element_type='klein4', the_one=None) is a thin wrapper over genome_append(…, kernel=True) (the deliverable a downstream teach-a-kernel → append loop was about to hand-roll). The header STILL records element_type so future non-klein4 content encodings ride a UNIFORM container. kernel_pack now WRITES v6; the v5 0x4B byte-TLV header is READ-ONLY back-compat.
THE DISTINGUISHER — option (a), a self-describing KERNEL TELOMERE (collision-FREE). Since the header leaf is all Klein-4 ({0,1,2,3}) it cannot use the byte->3 escape. The chosen distinguisher is a NEW cap marker KERNEL_TELOMERE_MARKER = 0x6B ('k', mnemonically paired with the v5 0x4B 'K'): a v6 kernel chromosome OPENS with a 0x6B kernel-telomere cap (instead of the plain 0x43 CHROM cap), and the header is the leaf at the reserved POSITION immediately after it. This is a FRAMING marker (a byte >3, like every cap), NOT in-band magic — so it is COLLISION-FREE: no content leaf can ever be mistaken for the header (unlike an in-band Klein-4 magic prefix, which would carry a 4⁻ᵏ false-positive rate — the #725 saturated-caps concern). The bare in-memory strand still SELF-DESCRIBES (§44): scan for 0x6B, uncouple the next turn, read the base-4 fields — no manifest, no external length. The distinguisher choice mirrors the §44 invariant that decided rc121's header-in-strand and rc115's hash-contract.
FORMAT v5 → v6, DUAL-READ (never break an existing genome). The block walker (_walk_region_blocks / C genome_block_len) and the scanners (_split_into_chromosomes / _rebuild_manifest_from_body / C genome_count_chroms / genome_scan_chroms / genome_scan_region) gain ONE branch — recognise 0x6B as a chromosome-start cap. kernel_unpack reads THREE formats in one walk: v6 (0x6B telomere → leaves[0] is the Klein-4 header), v5 (0x4B byte-TLV block, unchanged), and no-header (D = leaf_count × leaf_dim). Proven: a v5-header fixture reads byte-for-byte identically; every pre-rc126 genome reads UNCHANGED (the rc114 v2→v3 / rc121 v4→v5 dual-read precedent).
SAME-RC C PEER (srmech_genome.c). The walker + scanners + the append region-scan gain the 0x6B kernel-telomere case; the v5 0x4B byte-TLV still reads (back-compat). genome_append_kernel REUSES the existing srmech_genome_append (no new C symbol — the O(1) tail-extend just accepts a 0x6B-led region). The parser-rule-hash pre-images tracked the format version to v6. Python==C byte-identical (turns.bin + manifest.json) on the v6 kernel round-trip AND the genome_append_kernel append (native-vs-forced-pure differential, D = 256 / 1000 / 8192 / 999). JPL-clean, caller-arena, malloc-free; pedantic (-Werror//WX) clean in BOTH -DNDEBUG and asserts-live; the 81-assert C genome smoke green in both modes.
GATES. The v6 Klein-4-header round-trip EXACT for any D incl. non-leaf_dim-aligned (250 @ leaf_dim=100, 777, 65537); O(1) genome_append_kernel measured flat-time + tail-extend (prior body an exact prefix, no whole-body rewrite) + header preserved; the bare strand self-describes (recover D + element_type by scan, no manifest); v5 byte-TLV dual-read identical; no mixed-encoding smell (the store walks as one 0x6B cap + all 0x51 Klein-4 turns, no 0x4B block-kind); Python==C byte-identical. Full-suite gates: the 5th-SSOT version pin (test_signal_processing_scaffolding.py), test_mcp.py::test_all_param_types_json_coercible (no NEW advertised param type — genome_append_kernel rides str/Sequence[int]/HV), test_tool_schema_coverage, and the Rosetta ledger (genome_append_kernel non_compute, python_only_debt unchanged) all green; all 25 tools.total pins → 391. numpy-absent; ABI 3. 5 SSOT files rc125 → rc126.
[0.9.0rc125] - 2026-07-04¶
THE RECOVERABLE FOLD as a HarmonicMaass-style PAIR carrier (task #723 — "op⊗operand returns recoverability, built LITERALLY"; the direct follow-on to rc124/#697; +2 ops + 1 carrier, non_compute). tools.total 388 → 390 (+2: coupling.fold_encode_recoverable + op_provenance.lossy_projection_record; the RecoverableFold pair carrier is a return type — no ToolEntry, like HarmonicMaass; the coupling.fold_identity verdict is exempt from tool-schema coverage — its RecoverableFold operands are in-process carriers that cannot cross the JSON-RPC boundary, the greedy_bipartite_alignment / one.to_scalar precedent). NO new numerical kernel → NO new C peer (the carrier is DATA — confirmed); ABI stays 3; numpy stays absent; no abs(). 5 SSOT files rc124 → rc125.
THE RECOVERABILITY PRINCIPLE, MADE LITERAL. rc124 shipped fold_encode (EXACT) + fold_spectrum (a similarity/cleanup read verified by bit-identical RECONSTRUCTION — exact WHEN the fold has capacity, honest-unrecovered below the dim >= 4·n_pairs floor). rc125 makes recovery EXACT at ANY dim by ATTACHING the exact complement (the generating decimation R), following the field–excitation recoverability principle: a lossy projection is recoverable iff you attach the exact complement it dropped.
RecoverableFold(lossy_bundle, exact_seed_R, *, branches=None)— the PAIR carrier, MIRRORINGHarmonicMaass(hol, shadow)(rc71). WhereHarmonicMaassstores the (holomorphic-part, shadow) pair and "storing the shadow IS storing the completion" (the non-holomorphic completionf⁻is the Eichler integral of the stored shadow, recoverable not stored),RecoverableFoldstores(lossy_bundle ↔ hol, exact_seed_R ↔ shadow)and "storing R IS storing the recovery.".lossy_bundleis the rc124 lossy Klein-4 fold store;.exact_seed_Ris the exact decimationPoly(Nonefor a bare/"found" fold — a real-corpuscooccurrence_foldwith no generator);.complement()↔HarmonicMaass.xi(). Immutable,__slots__.fold_encode_recoverable(R, branches, *, dim, seed=0)→RecoverableFold. Produces the PAIR: the rc124 lossy bundle (viafold_encode, UNCHANGED — barefold_encodestill returns the bare dict, full back-compat) + the exact seedR. The chosen API mirrors theharmonic_maass(hol, shadow)constructor style (a dedicated constructor, keepingfold_encode's return type stable) over arecoverable=flag.fold_spectrumreads the EXACTRfrom the pair when present → re-derivesfractal_spectrum(R, branches)EXACTLY, at ANY dim — INCLUDINGdim=8(< the 4·n_pairs=16 gasket floor), where rc124's bare similarity read honestly returnsunrecovered. THIS IS THE POINT: exact recovery past the capacity wall, becauseRis CARRIED not decoded. The result isdict(fractal_spectrum(R, branches))bit-for-bit PLUS{verdict:'recovered', op_provenance:'EQUAL', recovery:'exact-seed', fold_consistency (==1, the integrity check that the carried R re-generates the stored bundle), similarity=1, confidence=1, identity}. When the seed is ABSENT (bare fold) → FALL BACK to rc124's similarity/cleanup read (unchanged; the honest-unrecoveredpath preserved).
THE FOLD-IDENTITY VERDICT (fold_identity(a, b) → EQUAL / NOT_EQUAL / UNKNOWN) — the one-sidedness is PRESENCE-vs-ABSENCE of the complement. Two RecoverableFolds are the same fold iff they recover the same (R, branches) — decided via each fold's identity() (the op_provenance canonical-hash of the fold_encode op with R + branches as the pinned EXACT inputs; rc117 machinery reused). EQUAL / NOT_EQUAL when BOTH carry the exact complement — the identity hashes are decidable because the inputs are EXACT (equal hash ⟹ EQUAL, different hash ⟹ a genuinely different recoverable object ⟹ NOT_EQUAL); dim/seed are NOT part of identity (two folds of the same (R, branches) at different dims recover the SAME object and share the address). UNKNOWN when EITHER fold lacks the complement — you CANNOT decide identity from a lossy bundle alone (identity is decidable only when you hold the complement); NEVER a false EQUAL/NOT_EQUAL from lossy bundles. This IS op_verdict's EQUAL/UNKNOWN one-sidedness — but the exactness of the carried complement UPGRADES it to the decidable EQUAL/NOT_EQUAL when both are present (op_verdict cannot answer NOT_EQUAL because program-equality is undecidable; here the OPERAND is exact, so inequality is decidable).
THE op_provenance REGISTRATION (scope-widening made concrete). fold_encode is EXACT-in/EXACT-out — its projection is HDC-SUPERPOSITION-COLLAPSE, a DIFFERENT projection from op_provenance's native float/asymptotic-tower truncation. It has NO tower and NO precision rung (recovery is exact at ANY dim because the complement is carried, not decoded). So the new op_provenance.lossy_projection_record(op, inputs) addresses it with family=None (no asymptotic target), rung={} (no precision rung), and a genuine NON-ASYMPTOTIC projection_kind='hdc' kind (_TOWER_HDC — recorded honestly, NEVER a faked interior/edge tower_kind). The op_provenance module scope wording is WIDENED from "value-inexact frontier" → "lossy-projection" (float-projection AND HDC-superposition-projection under one umbrella — the recoverability unification made concrete); the two DUAL faces (carry = value-inexact/asymptotic-tower; lossy_projection_record = exact-in/exact-out/carried-complement) are now documented as one frontier.
GATES. Exact recovery at dim=8 (where bare rc124 fails) via the carried seed == fractal_spectrum(R) bit-for-bit (the headline); fold-identity EQUAL (same R, different dim) / NOT_EQUAL (different R) / UNKNOWN (a bare fold on either side — never a false verdict from lossy bundles); rc124 bare-fold behavior UNCHANGED (similarity read + honest-unrecovered below the floor); the HarmonicMaass-shape mirror (.lossy_bundle ↔ hol, .exact_seed_R ↔ shadow, .complement() ↔ xi()); determinism; the op_provenance registration (family=None, no fake tower, scope-widened); the coverage + rosetta ledgers (both new ops + fold_identity non_compute) + the 5th-SSOT version pin + all 30 tools.total pins (→ 390) + the MCP coercer surface (no NEW advertised param type — fold_encode_recoverable rides Poly/int, lossy_projection_record rides str/dict). numpy-absent; ABI 3. 5 SSOT files rc124 → rc125.
[0.9.0rc124] - 2026-07-04¶
THE BIDIRECTIONAL HDC-FOLD ↔ SPECTRAL-DECIMATION TRANSLATION LAYER (task #697 — the "Q2 reader made LITERAL"; +2 ops, non_compute). tools.total 386 → 388 (+2: coupling.fold_encode + coupling.fold_spectrum, judged non_compute per the cooccurrence_fold / from_bodies precedent — pure orchestration over shipped Klein-4 HDC + Poly + fractal_spectrum ops, so NO new numerical kernel and NO new C peer); ABI stays 3; numpy stays absent; no abs(). 5 SSOT files rc123 → rc124.
THE TRANSLATION. fractal_spectrum(R, branches) (rc100; §Ch-2) reads a self-similar lattice's spectral-decimation structure from an EXPLICIT decimation Poly R. #697 makes the "Q2 reader" LITERAL — read the decimation from a STORED HDC FOLD, as a translation that runs BOTH directions. The two directions are ASYMMETRIC by the nature of HDC, and THAT asymmetry is the design:
fold_encode(R, branches, *, dim, seed=0)— params → fold: EXACT / total / deterministic. The decimationPolyR's coefficients + the branch count are role-filler bound into a single lossy Klein-4 bundle in thecooccurrence_foldstore shape (F584/F758): each coefficient slotc{i}(andbranches) gets a deterministicklein4_randomROLE code; each distinct coefficient VALUE gets a deterministic FILLER code (keyed by its'num/den'token); thefoldis theklein4_bundlesuperposition of the role⊗value binds. Returns{fold, roles, codes, coeff_slots, branch_slot, slots, dim, seed, n_pairs}(JSON-native once theHVvalues serialise, exactly likecooccurrence_fold).fold_spectrum(fold, *, log_terms=25, margin_floor=None, capacity_mult=None)— fold → params: a SIMILARITY / CLEANUP-MEMORY readout, NOT exact. The bundle is LOSSY BY DESIGN, so reading the decimation back is a cleanup-memory recovery: each slot binds the role back against the fold (klein4_unbundle= self-inverse XOR) and cleans the value-plus-crosstalk estimate up against the value codebook (argmax_token similarity(unbundle, codes[token])). Where confident, the recovered tokens rebuild R + branches and feed the SAMEfractal_spectrumorchestration → the IDENTICAL decimation dict, PLUS{verdict:'recovered', op_provenance:'EQUAL', similarity, confidence, fold_consistency, per_slot}.
THE HONESTY BOUNDARY (load-bearing) — NEVER a silent wrong Poly. A recovery is accepted ONLY when all three gates hold: (1) capacity — dim >= capacity_mult·n_pairs (default 4·n_pairs, the HDC bundle-capacity floor; below D ~ 2k the fold is degenerate and two assignments can collide to the same vector — Kanerva 2009); (2) separation — every slot's winner beats the runner-up by >= margin_floor similarity (default 1/10; baseline chance is 1/4); (3) self-consistency — re-bundling the recovered role⊗value binds reproduces the stored fold BIT-FOR-BIT (fold_consistency == 1, the op_provenance one-sided EQUAL self-check — because the encode is exact, a fully-correct recovery reconstructs identically). Any gate failing → the honest {verdict:'unrecovered', op_provenance:'UNKNOWN', similarity, confidence, fold_consistency, per_slot, reason, spectrum_open} with NO decimation Poly (#717 honestly-inexact / carrier-ladder project-error).
GATES. The round-trip DoD — the Sierpinski gasket R(z)=z(5−4z) round-trips confidently at high dim (recovered params == fractal_spectrum(R) bit-for-bit; fold_consistency==1; EQUAL), AND a low-dim/crowded read fires the honest unrecovered verdict (crosstalk, NEVER a silent wrong Poly) — validated across degree-2..4 decimation Polys with ZERO silent-wrong; the JSON-native MCP boundary (fold type dict, _identity coercer, values HV-or-list); encode determinism (seed-keyed, bit-identical); the coverage + rosetta ledgers (both new ops non_compute) + all 29 tools.total pins (→ 388); MPM: the fractal_spectrum Sierpinski attribution (Rammal 1984 / Fukushima–Shima 1992, OA arXiv:1505.05855) carries through unchanged (no new citation). numpy-absent; ABI 3. 5 SSOT files rc123 → rc124.
[0.9.0rc123] - 2026-07-03¶
THE PARALLEL is_zero PER-WORKER ARENA SLICE RIGHT-SIZING (task #706 — a BUILD-only C arena-sizing fix; NO verdict change, NO new op). tools.total stays 386; ABI stays 3 (additive-logic only, no exported-signature / wire-format change); numpy stays absent; no abs(). 5 SSOT files rc122 → rc123.
THE OVER-PROVISIONING. The CHIRALITY-PRESERVING parallel peer srmech_thetasum_is_zero_interpolation_parallel (opt-in SRMECH_THETASUM_PARALLEL_ISZERO) carved its caller arena as control + (nw+1)·(1.5·ws_bound2): every region — the nw worker slices AND the shared-root parse region — got a full ws_bound2 path bound PLUS a +50% "replay margin". Two wastes: (a) the +50% margin double-counted the peeled top levels (they ARE the path's top levels, already inside ws_bound2), and (b) the shared-root region only ever holds the parsed root terms (tip_parse_root = bind_term_arr(n_terms) + parse, NO DFS, NO base-case series) yet was sized as a full worker DFS+base slice.
THE SUB-TASK BOUND DERIVATION (why ws_bound2 is EXACTLY the per-worker bound — no margin). A worker RE-DERIVES its sub-problem by replaying its task PATH (task_len levels via tip_descend, each allocating the SAME combine+nodes+subst a serial DFS frame does) into its OWN arena slice, then runs the UNCHANGED ti_decide DFS on the peeled frontier. Peeling task_len variables leaves n_syms − task_len free, so the sub-DFS depth is ≤ n_syms − task_len + 2. Peak worker arena = replay(task_len levels) + sub-DFS(≤ n_syms−task_len+2 levels) + base_case ≤ (n_syms+2)·level_words + base_words = the DOMINANT terms of ws_bound2 ≤ ws_bound2. So a worker's high-water is the SAME full-root-to-leaf-path high-water the SERIAL DFS reaches (the parallel peer visits the IDENTICAL leaves, byte-for-byte) — ws_bound2 already bounds it; the +50% margin is provably redundant. The shared-root region is re-sized to the new tip_root_parse_bytes(n_syms, n_terms, max_thetas, cap) — a WIRE-shape-only bound (no interpolation degree), computed by the SAME formula in the ws sizer and in tip_dispatch (read off the wire w) so the carve agrees without tip_dispatch needing the degree params. New layout: control + root_parse + nw·ws_bound2.
MEASURED REDUCTION. Per-worker slice 1.5·ws_bound2 → ws_bound2 = 1.50× smaller (e.g. Warnaar Cₙ Lemma 2.2: 72.9 MB → 48.6 MB; wide+deep three-term: 41.6 MB → 27.7 MB). Total caller arena (nw+1)·1.5·ws_bound2 → control + root_parse + nw·ws_bound2 = 1.69× (nw=8) … 2.25× (nw=2) smaller (nw=4: 207.8 MB → 110.9 MB, 1.87×) — the budget clamp now fits more workers. HONEST RESIDUAL (no-magic-numbers): the larger "~13×" slice-vs-runtime-peak gap is base_words (the base-case dense-ℚ grid, 98–99.6 % of ws_bound2) being a conservative a-priori upper bound on the MAXIMUM POSSIBLE leaf — sized from root-global degree params (max_theta_sq_sum / max_abs_exp), reached IDENTICALLY by the serial and parallel paths (a worker peak measured 2.90 MB vs the serial 2.91 MB on wide+deep; leaf cases like Lemma 2.2 never even reach a base case yet must reserve it). That looseness is SHARED with the serial ws_bound2 and is NOT safely reducible for the parallel slice — the Σe² degree band is the soundness floor (#693) and shrinking it risks a false-≡0 / arena OVERFLOW; per the sizing discipline (over-provision is safe, under-provision breaks the op) it is left conservative. Only the provably-redundant margin + the mis-sized shared-root were removed.
WHY IT IS SAFE (no under-provision). Verdict-invariance is unchanged — a too-small slice would only DECLINE to the byte-identical serial path (never a wrong answer) — but a decline defeats the accelerator, so the shrink is proven not to under-provision: an instrumented build (peak pool_cur per worker + a worker-status / carve-overflow counter) over the full rc103 suite (524 parallel calls / 2 161 worker task runs, incl. the ≥300-case fuzz) AND a 500-case × {2,3,4,8}-width heavy stress at a 16 GB budget (parallel path forced, no Python-budget decline) recorded zero worker arena-carve overflows and zero C-level declines, every parallel verdict == sequential == pure. parse < depth·level_words < ws_bound2 = per, so root ≤ per structurally (asserted) — no cap needed, the layout can never invert.
SAME-rc, C-side only. All changes are in c/src/srmech_thetasum_interp.c (tip_root_parse_bytes + srmech_thetasum_is_zero_interpolation_parallel_ws_bound + tip_dispatch); JPL-clean, caller-arena, malloc-free, pedantic -Werror/-pedantic green on gcc + clang, both asserts-live and -DNDEBUG. The Python caller is UNCHANGED (it delegates the total sizing to the C ..._parallel_ws_bound); only three descriptive comments in _native.py were updated. tests/test_thetasum_is_zero_parallel_rc103.py (verdict-identity + chirality/order-invariance across widths) stays green.
[0.9.0rc122] - 2026-07-03¶
THE ThetaSum.is_zero FAST-PATH QUASI-PERIODICITY-CLASS KEY HYGIENE (task #694, anomaly A-1 — a BUILD-only path fix; NO verdict change, NO new op). tools.total stays 386; ABI stays 3; numpy stays absent; no abs(). 5 SSOT files rc121 → rc122.
THE ANOMALY. ThetaSum._is_zero_py groups the cleared numerator's terms by QUASI-PERIODICITY CLASS before the ±-pair three-term FAST PATH, then defers any miss to the exact structural-interpolation COMPLETION (_is_zero_interpolation). The class key was the FULL net period-multiplier monomial (thetasum._net_period_multiplier_exps, Rosengren Eq. 1.6 via Theta.canonicalize) — which carries, besides the genuine period-lattice x / y exponents, the exponents of the nome p (its p^{−k(k−1)/2} power), the base q (introduced by a shift_x / shift_y), and the elliptic parameters a, b, c, …. Those are all UNITS in the coefficient field ℚ(q,p)(params) — invertible, hence independence-blind — so including their exponents SPLIT one genuine quasi-periodicity character across buckets.
THE FIX. The fast path now keys on the new thetasum._quasi_period_class_key, which keeps ONLY the x / y exponents (the genuine period-lattice character). The shared _net_period_multiplier_exps is UNCHANGED — it still returns the full multiplier monomial, because its p-coordinate is the Class-L p-character block label that carrier_spectrum._block_of_thetas needs (that consumer is byte-for-byte untouched); only the is_zero CONSUMER was repointed.
WHY IT IS SAFE (verdict-invariance). The key is a fast-path BUCKETING only: _class_is_zero is EXACT (it certifies a bucket ≡0 solely via the genuine Weierstrass three-term reduction) and any miss defers to the complete decider, so merging or splitting buckets changes only the PATH, never the VERDICT. Proven across the full thetasum suite (every FT ₁₀E₉ / three-term / Warnaar Cₙ Lemma 2.2 interpolation verdict unchanged) and by a direct old-key-vs-new-key _is_zero_py equivalence over a true-zero + non-zero battery.
THE WITNESS. The over-split is REAL and the key coarsens it (fewer buckets after), demonstrated for BOTH a non-reducible shape (θ(x⁻⁵) and θ(x⁻³)·θ(x⁻⁴) share the x⁻²⁵ character but the full key splits them by p: −15 vs −16 → 2 buckets → 1) AND a reducible ±-pair shape (θ(ax±)θ(bx±) = (p:−2, x:−4) vs θ(ax±)θ(b·xa±) = (a:−2, p:−3, x:−4) → 2 buckets → 1). The reducible witness ALSO differs in the parameter a, so dropping only p is insufficient — the genuine character is recovered ONLY by keeping x / y alone.
SAME-rc C PEER (standing everything-mirrors). The C srmech_thetasum_is_zero ±-pair peer's key kernel ts_net_period_key (c/src/srmech_thetasum.c) gains the same unit-strip (zero every non-x/y coordinate after accumulation), so the C partition stays byte-for-byte the Python _quasi_period_class_key. Additive-logic only — ABI unchanged (3); JPL-clean (the strip loop reuses the file's proven signed-index pattern); pedantic -Werror / -pedantic green on gcc + clang. (The COMPLETE structural-interpolation peer srmech_thetasum_is_zero_interpolation does NOT use this key — it is the exact decider — so it needs no change.) tests/test_thetasum_net_period_key_hygiene_rc122.py is the witness + verdict-invariance + carrier_spectrum non-regression guard.
[0.9.0rc121] - 2026-07-03¶
THE SIZE-AGNOSTIC KERNEL TRANSLATION LAYER — store / recall a Klein-4 kernel of ARBITRARY dimension D through the genome, D SELF-RECORDED, reconstruction EXACT for any D, no dimension baked in (GitHub issue #1245 REOPENED / UPSTREAM §60 / F1036; siona uses 8192-dim Klein-4 kernels). tools.total 384 → 386 (+2: kernel_pack + kernel_unpack, judged non_compute per the from_bodies / cooccurrence_edges precedent); genome format v4 → v5; ABI stays 3 (an additive block-kind byte + additive symbols — no existing wire-format change, the rc114 v3 precedent); numpy stays absent; no abs().
THE DIVE (task #722). The genome quad-strand + HDC ops were ALREADY dimension-agnostic on storage/read — round-trips exact to 1M+ bits; LEAF_CAP=256 is planning-only, never assumed on the read path. Two gaps remained: W1 — the manifest recorded leaf_dim × leaf_count but NOT the kernel's TRUE length D, so a non-multiple-of-leaf_dim kernel zero-padded and recall returned leaf_count × leaf_dim symbols (D=1000 → 1024 recovered; the caller had to externally remember 1000 to trim); W2 — no shipped chunk/reconstruct op (encode_shape gave the criterion only). siona's kernel is confirmed Klein-4 (2-bit {0,1,2,3}), so the element codec is an identity no-op today.
THE SURFACE (W2 closed). genome.kernel_pack(data, *, leaf_dim=256, label='kernel', the_one=None, element_type='klein4') -> strand chunks the flat kernel into leaf_dim-wide leaves (final leaf zero-padded — encode_shape's ceil-division, generalised to leaf_dim), couples them through the_one into a telomere-capped chromosome, and inserts the §60 KERNEL HEADER right after the telomere. genome.kernel_unpack(strand_or_path, the_one=None) -> data reads the header, recalls the leaves, and TRIMS to the true D — recovering the exact kernel of ANY dimension with NO caller-supplied length. the_one defaults to a deterministic all-ones invariant the unpacker reconstructs from the header's leaf_dim (a genome PATH with a manifest resolves it from the cache instead).
THE §60 HEADER — IN THE STRAND (W1 closed). A new block kind KERNEL_HEADER_MARKER = 0x4B ('K') alongside CHROM 0x43 / GENE 0x47 / PACKED 0x51: a fixed-width leaf_dim-byte inline block, [0]=0x4B, [1:9]= true D (uint64 big-endian — 8 bytes dwarf any MB-scale kernel: 8192 needs 2, a 3.43 MB / ~1.37e7-symbol F1035 kernel needs 4), [9:13]=leaf_dim (uint32 BE), [13]=element_type (uint8 enum; 0 = klein4, a DECLARED enum so future element types slot in WITHOUT another format bump), NUL-padded to leaf_dim. RATIONALE — the header lives in the STRAND, not only the manifest (§44): the strand is the SSoT and the manifest a REBUILDABLE cache; _rebuild_manifest_from_body reproduces the manifest by body-scan, so true-D must live in the strand or a rebuild loses it (the rc115 hash-contract invariant). The 0x4B block is one more self-describing kind in the SAME walk — stored VERBATIM (never bit-packed), skipped by recall / partition / genome_window, and NOT counted in leaf_count.
FORMAT v5 — BACK-COMPAT IS STRUCTURAL (the rc114 dual-read pattern, one layer up). The block walker gains one 0x4B branch; a body with NO kernel header defaults to element_type=klein4, D = leaf_count × leaf_dim (today's exact behaviour), so every existing v2 / v3 / v4 genome — and any v5 body with no header — reads UNCHANGED, NO migration (proven against the committed rc113 v2 fixture + a plain header-less chromosome). Reading never rewrites; an append / in-place edit migrates to the v5 writer.
THE SHARP-EDGE GUARD (the dive flagged it). HV.from_sequence(sectors=4) silently ACCEPTS symbols >3 in memory but the disk packer rejects them — so kernel_pack validates symbols ∈ {0,1,2,3} UP FRONT with a clean error naming the offending symbol + position (tested), not a deferred pack-time abort.
SAME-rc C PEER (standing everything-mirrors). The genome_block_len walker in c/src/srmech_genome.c gains the 0x4B case (a leaf_dim-wide block), and the two data-turn counters (genome_scan_chroms, genome_scan_region) exclude it — the 2-bit lane codec already exists in C (the v3 packer), so klein4 needs no new codec kernel. Python==C byte-identical on the kernel round-trip — turns.bin AND manifest.json — at D = 256 / 1000 / 8192 / 65536 / 1,000,000 (native-vs-forced-pure differential). ABI unchanged (additive block-kind byte only); JPL-clean; pedantic -Werror green Release (NDEBUG) + Debug (asserts-live). The manifest parser_rule_hash preimages track the version (genome_persistence/v5, genome_chromosome/v5) in both languages.
GATES. The DoD round-trips (256 / 1000 / 8192 / 65536 / 1 MB-scale, in-memory + on disk, INCLUDING the non-multiple cases 1000 / 1,000,000 with no external trim — W1); the §60 header layout; the symbol-range guard; Python==C byte-identical; a header-less body reads as klein4 / full-dim (back-compat); the coverage + rosetta ledgers (both new ops non_compute) + all 29 tools.total pins (→ 386); the genome suites green asserts-live AND -DNDEBUG; numpy-absent; ABI 3. 5 SSOT files rc120 → rc121.
[0.9.0rc120] - 2026-07-03¶
THE PER-OP CARRIER CONTRACT — make the per-op carrier RUNG machine-readable so a driver routes carriers WITHOUT a hardcoded op→rung name-map (GitHub issue #1254 / UPSTREAM §87 / F1041; closes the #1248 + #1239 + §87 loop). tools.total stays 384 (metadata only — a descriptor FIELD, not a new callable); ABI stays 3 (Python-side; no C compute, no C change); numpy stays absent; no abs().
THE GAP (introspected on rc117). The DSL/schema was already SSoT for chaining (dsl.Chain), composition (dsl.make_class), and the carrier LADDER (carrier_ladder_descriptor(), rc116 — the rungs Poly1/BiPoly2/TriPoly3 + R1/C2/H4/O8/S16 and the promote/project op paths). The ONE thing NOT machine-readable was the per-op carrier RUNG — which op consumes/produces which rung: octonion_conjugate's "8" was PROSE in the param summary, and qm.* / cd ops carried no DSL descriptor at all. So a driver (Siona) had to INFER the rung from the op NAME (a hardcoded octonion→8 map) or sniff register-sequence length — the last hardcode between the driver and fully-declarative carrier routing.
FORM CHOSEN: (2) — the descriptor ops map (the issue's second option; picked over form (1) ToolParameter carrier/rung fields). Rationale: ONE SSoT surface, living where the ladder facts already live (srmech.amsc.carrier_ladder); NO per-ToolParameter churn across the whole schema and no frozen-dataclass wire-format change; and it makes octonion_conjugate → rung 8 and cd_promote → variadic cleanly readable without a name-map. Because the contract is a descriptor FIELD (not a new callable), tools.total is unchanged and there is no rosetta/coverage delta.
THE SHAPE. carrier_ladder_descriptor() gains a third view, "ops": for each op leaf name, {"tool": <full ToolEntry name>, "consumes": <slot>, "produces": <slot>}. A slot is a LADDER slot {"ladder", "rung"} or a non-ladder slot {"ladder": None, "type"} (Mat/float/bool/list/dict/scalars — carriers OUTSIDE the promote/project ladders). A rung is a fixed int, "any" (variadic), "same" (ladder endomorphism), "arg:<param>" (rung equals a call argument's value), or "step_down" (one rung down the ladder). Verbatim: ops["octonion_conjugate"] = {"tool": "srmech.qm.octonion.octonion_conjugate", "consumes": {"ladder": "cayley_dickson", "rung": 8}, "produces": {"ladder": "cayley_dickson", "rung": 8}}; ops["cd_promote"] = {"tool": "srmech.amsc.cascade.cd_promote", "consumes": {"ladder": "cayley_dickson", "rung": "any"}, "produces": {"ladder": "cayley_dickson", "rung": "arg:dim"}}.
COVERAGE (29 ops). The cd family — qm.octonion (7: conjugate/norm/left_mult/right_mult/exp/exp_series_truncate/twiddle, fixed rung 8), qm.quaternion (7, fixed rung 4), generic cascade.cd_* (5: cd_mult/cd_conjugate/cd_norm_sq/left_mult_kernel/left_mult_is_invertible, variadic "any"), and the promote/project pair (cd_promote "arg:dim", cd_project "step_down"); the variable-ladder promote/project (poly_/qpoly_); and the prose constructors (bipoly_/tripoly_/qpoly_/qbipoly_from_coeffs, each producing a fixed rung). Deliberately deferred (reported): the *_mult_table / *_table_attestation Class-A metadata ops; cd_basis_product (a structural cocycle over dim+indices, not a carrier element); is_division_algebra_dim (a ladder-META predicate over a dim int); sedenion_zero_divisor_witness + sedenion_register (dict-/object-wrapped, not bare carrier slots — the S=16 rung is still reachable via the "any" cd ops + the ladder table); and the Class-L / spectral Mat/Vec numeric-carrier ops (those carriers are NOT part of the polynomial/Cayley–Dickson promote-project rung ladders).
THE RUNG-FROM-ARG + VARIADIC + STEP-DOWN cases are encoded as string grammar the driver resolves against the actual call and the ladder's own rungs values: "arg:dim" → int(args["dim"]); "any" → works at any rung (never promoted past); "step_down" → the largest ladder rung below the input; "same" → the input rung. A _resolve_rung reference resolver in the test IS the driver simulation — it contains NO {op: rung} dict.
GATES. The DoD driver-simulation test (reads octonion_conjugate → rung 8 + cd_promote → variadic and routes a dim-4 quaternion into octonion_conjugate end-to-end, resolving every rung through the declared grammar only — no name-map); the self-consistency test (every op's tool resolves to a real ToolEntry, its leaf key matches, and every INT rung it references is a real rung of its ladder — a cayley_dickson rung-8 op references the SAME 8 the octonion 'O' declares, so the two surfaces cannot drift); coverage ratchet + rosetta + all tools.total pins green (unchanged 384); the rc116 carrier-ladder suite green (additive-only); numpy-absent; ABI 3 unchanged. 5 SSOT files rc119 → rc120.
[0.9.0rc119] - 2026-07-03¶
THE #712 DZHANIBEKOV HARMONIC⊗SUBHARMONIC HALF-PERIOD READERS on the EllRatio carrier — three shipped ops grounding the harmonic⊗subharmonic cascade thesis (GitHub task #713). tools.total 381 → 384 (three genuinely new public ops); ABI stays 3 (one additive C symbol); numpy stays absent; no abs() (sign is Class-K).
THE THESIS. The torque-free (Dzhanibekov / tennis-racket) rotation's Jacobi sn/cn/dn map to EllRatio theta quotients under the #712 bridge (w = e^{iζ}, x = w², carrier-p = q_c²; DLMF 22.2/20.5). Its solution two-torus is a CYCLE OF CYCLES with two independent half-beats (DLMF 22.4). The #712 probes (committed dzhan_q1…q5, the in-repo SSoT + exact-ℚ oracle) established the exact carrier semantics; rc119 turns them into attested, tested, C-mirrored ops.
THE OPS.
ellbase.half_shift_response(ratio, axis, var=None)(alsoEllRatio.half_shift_response) — the exact monomial MULTIPLIER the carrier acquires under a HALF-period translation.axis'real'/'2K'→ the double-cover deck transformationvar ↦ −var(z↦z+2K⇔w↦−w): the pure Class-K sign(−1)^{var-parity of the prefactor}(defaultvar='w', the subharmonic half-var); bare iff every theta arg is EVEN invar.axis'nome'/"2iK'"→ the carrier PERIOD shiftvar ↦ p·var(z↦z+2iK'⇔x↦p·x): the−x⁻¹-typeTheta.canonicalizequasi-periodicity prefactor (defaultvar='x'). The EDGE-relationship read — exact even where the theta value is transcendental (the fullpshift()already exists; this is the HALF + reads ONLY the multiplier). COMPUTE,c_dispatched. Same-rc 1:1 C peersrmech_ellratio_half_shift_response(theis_elliptic-peer marshalling + an axis selector; the C multiplier EQUALS the pure-PythonEllMonomialbyte-for-byte). Oracledzhan_q2: sn/cn/dn real-2K →−1/−1/+1; nome pshift theta-parts →q/−q/−1.ellbase.chirality_parity(ratio, var='w')(also a method) —'even'(HARMONIC — closes under a SINGLE 2K half-beat, like dn: real period 2K) vs'odd'(SUBHARMONIC — needs 4K / the PAIR, like sn/cn: real period 4K, half the harmonic frequency). Read as the parity of the prefactor'svar-exponent: EVEN ⇔ real-2K response+1(boundary-blind — cannot see the flip); ODD ⇔−1. A thin structural read (non_compute). Oracledzhan_q3/dzhan_q4: sn/cn odd, dn even; every quadratic/intensity observable (sn²/cn²/sn·cn/sn·mirror(sn)/triple) even → boundary-blind.ellbase.beat_relation_residue(ratio, var='x')(also a method) — the exact BEAT-RELATION residue on the HARMONIC (x) frame: the nome-axis mismatch monomial that RECOVERS the beat relationp = q_c²(the harmonic torus nome is the SQUARE of the subharmonic half-step). For the harmonic sn² objectx⁻¹·θ(x)²/θ(q·x)²the residue is exactlyq²·p⁻¹— the coherence residue the carrier surfaces unprompted (is_elliptichonestly declines it; the residue is unity ⇔p=q²). A thin structural read composing the carrier period-shift (non_compute). Oracledzhan_q4: residueq²·p⁻¹; closes atq=3/7, p=9/49.
THE TWO AXES + THE PAIR-CLOSURE. The real 2K axis is a pure Class-K sign (−1)^{w-parity}; the nome 2iK′ axis is the −z⁻¹ pshift prefactor from the theta quasi-periodicity. dn = chirality-EVEN harmonic (closes under one 2K half-beat); sn/cn = chirality-ODD subharmonic (4K, half frequency — needs the pair). Neither chirality closes alone; the chirality-even (quadratic) readers are boundary-blind. The FLIP = hold dn, flip sn/cn = the Klein-4 action (the Euler-equation sign-flip symmetry = the three half-period torus translations; dzhan_q5).
C PEER. srmech_ellratio_half_shift_response reuses the srmech_ellratio_is_elliptic machinery (arena, er_parse/er_build/er_pshift_arg/er_args_eq) + a new er_compute_negate (the real-axis var↦−var Class-K sign flip) + er_emit_multiplier (the shift.pref / self.pref monomial marshalled back over the interned symbol table). Caller-arena, malloc-free, no abs(), JPL-clean; additive symbol → ABI stays 3.
GATES. Each op reproduces its #712 probe oracle exactly; half_shift_response Python==C byte-for-byte (the #712 objects + a valid-input fuzz — 1550/1550 agree; the only residual py=ERR cases are a PRE-EXISTING Theta.canonicalize/Q.__pow__ negative-base limitation shared by the shipped pshift/is_elliptic, NOT a rc119 divergence); the flip=Klein-4 + boundary-blind tests green; coverage ratchet + rosetta + all pins green; the ellbase/thetasum/elliptic suites green; numpy-absent; JPL + pedantic clean both modes. 5 SSOT files rc118 → rc119; tools.total 381 → 384 (+3 ToolEntries); rosetta +half_shift_response (c_dispatched) +chirality_parity/+beat_relation_residue (non_compute).
[0.9.0rc118] - 2026-07-03¶
ROOT-FIX an over-strict assert in srmech_bigint_pow_bound that aborted 0^{exp>0} in asserts-live builds (task #715). tools.total stays 381; ABI stays 3 (no surface change — a one-line C-assert correction); numpy stays absent; no abs(). VERDICT (a): OVER-STRICT ASSERT, not a latent bug — the math was always correct.
THE ABORT. tests/test_unary_theta_rc70.py::test_python_c_parity_stress_sweep aborted (Fatal Python error: Aborted) on ANY asserts-live build (gcc -O2 -std=c11 WITHOUT -DNDEBUG); CI's Release (-DNDEBUG) builds strip assert() and never saw it. The failing assert was c/src/srmech_bigint.c:127: assert(base_n > 0u || exp == 0u); in srmech_bigint_pow_bound. Triggering input (deterministic): the stress-sweep spec unary_theta("trivial", j=1, a=1, b=0, D=1, support="all", N=40) — support="all" includes n=0, and with j≥1 the term is χ(0)·0^j, so the C peer asks pow_bound for the limb-bound of 0^1.
WHY IT IS OVER-STRICT (case a). 0^{exp>0} = 0 is a well-defined value the code ALREADY computes correctly: pow_bound's own body returns 1u for base_n == 0 (line 129), and srmech_bigint_pow_u32 square-and-multiplies 0^1 → 0 exactly. The base_n > 0u || exp == 0u precondition forbade the one valid case its own body handles. It mirrors the pure-Python oracle exactly (chi * (0 ** j); Python 0**1 == 0).
TRUST — was any shipped (NDEBUG) output ever WRONG? NO. Under -DNDEBUG the assert is a no-op, so the correct 0^j → 0 path ran: the triggering input yields byte-identical C==Python coefficients (all-zero — the two-sided odd-j trivial theta cancels n^1 + (-n)^1 = 0, and the n=0 term contributes 0). No wrong math, no UB (the 0-value carrier needs 0 limbs; pow_bound's 1u cap is ample). The bug was a false abort in asserts-live builds only — the shipped Release wheels were always correct.
THE FIX. Replace the false precondition with the true one: assert((size_t)exp == exp) (representability — kept) + a postcondition assert(prod + 1u > prod) after the overflow guard (a valid, non-wrapping limb count). JPL Rule 5 (≥2 asserts) preserved; the 0^{exp>0} and x^0 cases flow through the existing return 1u. Root-fix in shared bigint infra → every pow caller benefits.
SIBLING AUDIT (the rc36 pow_u32 lesson — shared-infra bound bugs have siblings). The over-strict pattern was UNIQUE to srmech_bigint.c:127. Its fingerprint was already visible in the callers: srmech_bigexp.c:266 and srmech_jacobi.c:337-339 pass q_limbs == 0u ? 1u : q_limbs to pow_bound — defensive guards that route AROUND the abort (now unnecessary, left in place as harmless; pow_bound(0, exp) returns the same 1u). The runtime pow_u32 callers are srmech_eisenstein.c:344,353 (base is a divisor d ≥ 1 / cofactor n/d ≥ 1, never 0 → safe today, now robust) and srmech_unary_theta.c:230 (the trigger). No other base>0 || exp==0-shaped assert exists (the apagodu_zeilberger / zeilberger total>0 || nrow==0 asserts are unrelated data-structure invariants).
GATES. test_unary_theta_rc70.py green BOTH modes (18/18 asserts-live AND -DNDEBUG); the theta/bigint consumer suites (unary_theta, eta_quotient, eisenstein, qrow rc113, harmonic_maass, poly, jacobi, thetasum) green (182 passed); pedantic -Wall -Wextra -Wpedantic -Werror clean BOTH modes (the NDEBUG no-op assert leaves no unused variable); JPL audit green; full suite green both modes. 5 SSOT files rc117 → rc118.
[0.9.0rc117] - 2026-07-03¶
OPERATORS⊗OPERANDS AS ONE ADDRESSABLE OBJECT — the op-carrying carrier (srmech.amsc.op_provenance; the capstone of the three-dive arc: dive #718 prototype → dive #719 duality archaeology → the joint review). tools.total 376 → 381 (five genuinely new public ops); ABI stays 3 (additive C symbols only); numpy stays absent; no abs(); no raw hashlib.
THE IDEA (user, verbatim intent). "Our operations, like language and all other math, are exact — but the answers approach some asymptotic limit, even if it happens to close bit exact. In areas where we will lose exactness, if we can carry knowledge of the operation as well, we can address not just operands but also operations." The value of an inexact-frontier op is a PROJECTION; the exact generating operation is the SSOT. rc117 attaches the operation to the result — the two truths (operand/operator, field/excitation) held in ONE addressable object.
THE OPS.
op_provenance.carry(op, inputs, params, family=)— run a registered frontier op AND attach its exact generating operation:{'value', 'inputs' (canonicalised), 'provenance' {op, params, input_sha256, family, rung, leaves_exact, chain_sha256}}. The registry covers the rc117 value-inexact frontier: the Class-Nseries_truncatefamily (sin/cos/exp/log1p/atan) +best_rational+ the Class-L float64 producers (jacobi_eigvals/symmetric_eigendecompose/hermitian_eigendecompose/heat_trace/resonant_spectrum). Existing op signatures are UNTOUCHED — provenance is an opt-in entry point over a name-keyed registry (the genome op-log / DSLrun_toml_chainre-run-by-name model), chosen over a carrier field (Mat/Vecare__slots__zero-copy buffer classes; the series ops return bare tuples — no uniform host) and over aprovenance=kwarg (return-shape forking on a flag). Param DEFAULTS are MATERIALISED into the record, so a default and an explicit-default call carry ONE operation address.op_provenance.op_provenance_hash(record)— the Class-A canonical hasher: SHA-256 of the MPRRecord-style canonical byte image (json.dumps(record, sort_keys=True, ensure_ascii=False)— the SAME conventionMPRRecord.to_json_lineand the genome manifest use), EXCLUDING the record's own cachedchain_sha256. Float-free by construction: floats ride as{'__float64__': float.hex(x)}exact-bit-pattern tags, beyond-int64 ints as{'__bigint__': '<decimal>'}, rationals as{'__rational__': [num, den]}— raw floats are REJECTED, never silently forked (C%.17gdoubles are not byte-identical to Pythonrepr). Same-rc 1:1 C peersrmech_op_provenance_hash(+srmech_op_provenance_hash_arena_bytes):srmech_json_parse→ stripchain_sha256→ canonicalsrmech_json_write_ws→srmech_sha256_hex— the IDENTICAL digest from ANY JSON formatting of the same record, with the same lexical raw-float rejection (the mirror agrees on the DOMAIN, not just the values). Caller-arena, malloc-free, JPL-clean; additive symbols → ABI stays 3.op_provenance.op_verdict(p1, p2)— the honest verdict pair, side 1 (the load-bearing contract)."EQUAL"iff the canonical chain hashes agree (recomputed, never trusting the cache): same generating program + same pinned inputs ⟹ same ideal object BY CONSTRUCTION — sound even where the float readouts diverge in the last ulp (platform divergence is a projection artifact, not a different object)."UNKNOWN"otherwise — equality of programs is UNDECIDABLE, so a different chain proves nothing (an algebraically-equal but syntactically-different cascade honestly stays UNKNOWN; a coincidentally-equal VALUE from a different op is never called EQUAL). NEVER a false "UNEQUAL" — the asymmetry IS the contract, documented as such.op_provenance.family_verdict(p1, p2)— side 2."SAME_TARGET"iff both carry a family and the full family address agrees (sametarget_idAND sametower_kind);"UNKNOWN"otherwise — never a false "DIFFERENT".op_provenance.reproject(provenance, overrides=, inputs=)— re-run the carried operation at a different rung: the value RE-COMPUTED from the operation-as-SSOT (a sin(1) carrier reprojects from N=3 to N=12, the exact rational sharpening toward the named target). Inputs are RE-VERIFIED against the record'sinput_sha256before anything runs (the MPM re-verification: a provenance that can't be re-verified is broken); ONLY rung params may be overridden (a non-rung override would change the target — a different operation, not a re-projection); the family address is PRESERVED (rung-independent by construction).
FAMILY vs INSTANCE — resonator vs rung (dive #719 + the joint review). family = (asymptotic TARGET id, TOWER-KIND); instance = (family, chain_sha256, rung). TOWER-KIND is a real two-valued choice from the mathematics: "interior" (Taylor-type additive/smooth approach — the series_truncate ops) vs "edge" (CF-type multiplicative/reciprocal approach — best_rational convergents, and the Jacobi eigensolve towers whose rungs COMPOSE rotations). Precision params (num_terms, max_denominator, tolerance) are the RUNG INDEX — excluded from the family address, included in the instance. N truncations of sin(1) = ONE family address + N instance addresses: the asymptote is addressed by its generator. The same target approached by the interior and the edge towers is two DIFFERENT families (tested).
THE FAMILY NAMESPACE = the attestation registry (MPM as the operation-identity authority). A family target_id must be a NAMED/ATTESTED object — derived from exact inputs ("sin(1/1)" from the reduced sign-normalised rational point, so sin(2/2) and sin(1/1) name ONE target; "eigvals(sha256:<hash>)" from an exact matrix's canonical content-address) or caller-attested via family= (tower_kind stays op-intrinsic, never overridable). Family-equality is decidable exactly when targets are named; unnamed targets get NO family — instance-only.
EXACT LEAVES. Inputs must bottom out in exact data (ints / rationals / strings / {'__exact_ref__': <64hex>} content-hashes of exact carriers). A float leaf weakens the guarantee to same-op-on-same-bit-pattern — recorded honestly as leaves_exact: False (the float rides as its EXACT float.hex bit pattern: well-defined, platform-stable, honestly inexact), and drops the derived family (an inexact-leaf target is unnamed).
Scope. +5 ToolEntries (tools.total 376 → 381; the 26 pin files updated); Rosetta ledger +5 rows (op_provenance_hash → c_dispatched; carry/op_verdict/family_verdict/reproject → non_compute registry/verdict dispatch — all numerical compute delegated to registered ops + the C-backed hasher; debt ceilings unchanged). MCP: no new coercers needed (str/dict param types pre-exist). Tests: +1 file (test_op_provenance_rc117.py — the three #718 wins: (i) op-EQUAL where value-equality fails (±1-ulp simulated platform divergence) + the coincidental-value boundary (UNKNOWN, never false-EQUAL); (ii) re-projection N=3→N=12 verified against the direct call; (iii) one family / three instances + interior-vs-edge different families; (iv) leaves_exact honesty; (v) Python==C hash parity on identical records incl. non-canonical formatting — native-guarded). 5 SSOT files rc116 → rc117.
[0.9.0rc116] - 2026-07-03¶
The CARRIER CONVERSION LADDER — the orphan fix + promote/project rungs + the ladder descriptor (issue #1248, F1038). tools.total 367 → 376 (nine genuinely new public ops); ABI stays 3 (no C — every op is non_compute carrier restructuring); numpy stays absent; no abs(); no float.
THE ORPHAN FIX. The tool_schema producer/consumer census (measured on rc113) found two carriers ORPHANED — CONSUMED but never PRODUCED from the registry: BiPoly (consumed by zeilberger + wz_certificate) and TriPoly (consumed by apagodu_zeilberger). rc113 shipped only the q-side (qbipoly_from_coeffs), so the classic non-q Zeilberger / WZ / Apagodu row could not be built by prose. rc116 adds the missing constructors:
zeilberger.bipoly_from_coeffs(coeffs)— ak-ascending list of INTEGER-LEAFn-coefficient lists → the exact-ℚ[n,k]BiPoly([[1, 1], [-1]]is(1+n) − k; the rc42F(n,k)=C(n,k)term ratios[[1, 1]] / [[1, 1], [-1]] / [[0, 1], [-1]] / [[1], [1]]→zeilbergercertifiesf(n+1)−2f(n)=0forΣC(n,k)=2ⁿ).tripoly.tripoly_from_coeffs(coeffs)— thebipoly_from_coeffsgrammar recursed one level (aj-ascending list ofk-ascending lists ofn-lists) → the exact-ℚ[n,j,k]TriPoly([[[0, 1]], [[1]]]isn + j).
Both mirror the rc113 qbipoly_from_coeffs shape exactly; ints only (a float / bool / str leaf is an honest TypeError); non_compute BUILDERS (the coupling.from_bodies / text.cooccurrence_edges precedent — they construct an operand; every computation lives in the ops that consume it).
THE VARIABLE LADDER — promote / project. A univariate IS trivially bivariate, but no op crossed the rungs. rc116 adds them so a driver (siona's result register, F1024) can auto-route a lower-rung carrier UP to any higher-rung consumer:
carrier_ladder.poly_promote(p, n_vars)/poly_project(p)—Poly(k) ↔ BiPoly(n,k) ↔ TriPoly(n,j,k).carrier_ladder.qpoly_promote(p, n_vars)/qpoly_project(p)—QPoly(x=qⁿ) ↔ QBiPoly(X=qⁿ, Y=qᵏ).
PROMOTE is the trivial embedding — it adds a degree-0 variable (Poly↪BiPoly adds n, BiPoly↪TriPoly adds j, QPoly↪QBiPoly adds Y), so the polynomial is unchanged as a function; TOTAL (a no-op when the target rung equals the current). PROJECT drops the highest-rung variable IFF genuinely trivial (degree 0); when the variable is genuinely PRESENT it raises a coherency error that NAMES the obstruction ('n' is the genuinely non-trivial variable) — NEVER a silent truncation (the rc104 lesson; the beat-relation diagnostic style). ROUND-TRIP LAW (tested): project(promote(x)) == x, EXACT, at every rung.
THE HURWITZ LADDER — cd_promote / cd_project. The same shape one level of algebra up, shipped next to the cd_* family:
cascade.cd_promote(x, dim)— zero-pad the higher imaginary half,x ↦ (x, 0): ℝ↪ℂ↪ℍ↪𝕆↪𝕊. This is the SAME subalgebra embedding the qm.octonion/quaternion restriction tests exercise (a quaternionq₄sits in 𝕆 asq₄ ⊕ 0₄under the sharedcd_basis_productcocycle — ℍ is the top-4 of 𝕆). A consistency test provescd_multon promoted quaternions matchescd_promoteof the quaternion product, andoctonion_left/right_mult(cd_promote(q₄,8))hasquaternion_left/right_mult(q₄)as its top-left 4×4 block (the rc109 pattern).cascade.cd_project(x)— realify DOWN one doubling IFF the higher half all vanish (a complex(a,0)IS the reala); else the coherency error NAMES the genuinely-present component.cd_project(cd_promote(x, 2·d)) == xEXACT.
THE LADDER DESCRIPTOR. carrier_ladder.carrier_ladder_descriptor() — a small declarative coherency map: {'carriers': {<carrier>: {'ladder', 'rung'}}, 'ladders': {<ladder>: {'rungs', 'adds_variable', 'promote', 'project'}}} over the three ladders (variable, variable_q, cayley_dickson), so the routing driver reads which promote/project op to call to lift a carrier to a consumer's rung.
Scope. All nine ops are non_compute — pure carrier restructuring (trivial embed / drop; no numerical kernel), so no C peer (the from_coeffs precedent) and ABI stays 3. MCP coercers added for the new param types (list[list[list[int]]], Poly | BiPoly, BiPoly | TriPoly). Rosetta ledger +9 non_compute rows (debt ceilings unchanged). MPM: in-repo SSOT (the carriers' own conventions; Baez 2002 for the CD basis convention, already cited). Tests: +1 file (test_carrier_ladder_rc116.py, 24 tests — the constructor round-trips, the round-trip law at every rung of both variable ladders + the Hurwitz ladder, the naming coherency errors, the octonion/quaternion-convention consistency, the registry-driven capstone: registry-built BiPoly → zeilberger recurrence + promoted Poly → zeilberger + the naming error); the 25 tools.total pins 367 → 376. 5 SSOT files rc115 → rc116.
[0.9.0rc115] - 2026-07-03¶
Genome O(1)-AMORTIZED append + non-quadratic genome_pack — the region-chain hash contract, format v4 (issue #1245 ask (b), UPSTREAM §56, the DIRECT follow-up to rc114's format v3). tools.total stays 367 (no new public op — an append/pack algorithmic change + a manifest-versioned format); ABI stays 3 (additive/internal C only — no exported-symbol wire format changed; the on-disk FILE format is versioned in the manifest); numpy stays absent; no abs(); no float.
THE PROBLEM. genome_append rewrote the WHOLE turns.bin and re-hashed the WHOLE body on every append (via genome_save), so N appends of the 1,024-leaf chromosome were O(N²) — the F833 super-linear wall (rc114 native baseline on this machine: 0.131 → 0.156 s/append and climbing). genome_pack was worse: it imported each .chr in a loop, re-reading + re-hashing the growing dest body once per bundle (also O(N²)). The whole-body body_sha256 (an attestation surface) cannot be recomputed in O(1) on append, which is exactly why rc114 kept it and split (b) out.
THE HASH CONTRACT — CHOICE (ii), the REGION CHAIN (format v4). The manifest gains a regions array — one {byte_offset, byte_len, sha256} entry per chromosome, its full-region digest — and body_sha256 becomes the region chain Hₙ = sha256(Hₙ₋₁ ‖ regionₙ) seeded by H₀ = sha256(b""). This is O(1)-maintainable on append (extend the head from the prior body_sha256 + the new region digest), re-verifiable from the file (re-hash each region, re-fold — a flipped/truncated/re-ordered byte fails), AND body-derivable by a §44 scan (a pure function of the body + leaf_dim). We chose (ii) over (i)-lazy because (ii) uniquely preserves §44: choice (i)'s body_sha256_current staleness flag is NOT derivable from the body bytes, so an append-written manifest would no longer be reproducible by rebuild-by-scan — breaking the genome's load-bearing "manifest is a derived cache; rebuild == written" invariant. Per-chromosome regions make the chain scan-reproducible AND unify the region digest with the existing provenance unit — regions[i].sha256 == the chromosome's .chr bundle region hash == what genome_register_attested already treats as the AMSC attestation (verified: genome_register_attested + the op-log semantics are UNCHANGED — the region hash IS the provenance unit, as strong as a body hash). attestation.response_sha256 stays a valid re-verifiable 64-hex (the chain head). format_version 3 → 4; v2/v3 manifests stay READ-compatible (no regions → the legacy whole-body sha256(body) == body_sha256 check; the rc114 dual-read pattern).
THE APPEND (O(1)). genome_append now TAIL-EXTENDS turns.bin (append-only; prior bytes NEVER read/rewritten/re-hashed) and updates the manifest by APPENDING one chromosome entry + one region entry and EXTENDING the chain in O(1). Per-append cost is bounded by the NEW chromosome's own encoding + the small O(n_chromosomes) manifest rewrite — NOT the genome's total size. Appending to a legacy v2/v3 genome migrates it to v4 once (a single full rebuild), then subsequent appends are O(1). A faster pure-Python bit-packer (_pack_turn_block via a 256-entry 4-symbol lookup, byte-identical) cuts the remaining per-append constant ~4×.
genome_pack (single-pass, linear). Reads every .chr region ONCE in canonical sorted-label order, concatenates them, and writes turns.bin + rebuilds the manifest ONCE (the full-body re-hash belongs here — a canonical single build). C and pure Python both single-pass.
THE DoD NUMBERS (measured; generating code committed at docs/srmech/notes/rc114_genome_bitpack_bench.py). Appends of the 1,024-leaf chromosome now run FLAT + far below 0.2 s: ~0.046 / 0.048 / 0.050 s/append at 10 / 20 / 40 (40-vs-10 per-append ratio ~0.9 — O(1) amortised, no climb), vs the rc114 baseline (0.213 / 0.203 / 0.243 on the issue machine; 0.131 → 0.156 native here). genome_pack scales LINEARLY (s/MB constant across a 4× body sweep 0.67 → 2.67 MB: 20→40 chromosomes = 0.082 → 0.167 s ≈ 2× for 2× body), round-trip EXACT leaf-for-leaf across append+pack.
Same-rc C mirror (everything-mirrors), byte-identical. c/src/srmech_genome.c: the v4 manifest builder emits regions + the chain (genome_fill_regions_chain/genome_chain_regions/genome_build_region); srmech_genome_append is rebuilt to tail-extend + rebuild the manifest from the parsed old manifest + the new region (genome_append_v4 / genome_append_fill, parse-once arena layout — never touches the whole body), with a one-time v2/v3 migration fallback; the whole-body integrity bound is format-aware (genome_verify_body — chain for v4, whole-body digest for v2/v3) across load/remove/replace; srmech_genome_pack is single-pass (genome_pack_concat_save). SRMECH_GENOME_FORMAT_VERSION 3 → 4 + the parser_rule_hash pre-images track v4 in srmech.h. Native-vs-forced-pure differential is byte-identical (save/append/load/remove/replace/explode/pack/rebuild-by-scan manifests + bodies). ABI stays 3 (additive symbols only; the manifest FILE format is versioned). JPL-clean (caller-arena, malloc-free, ≤60-line fns, ≥2 asserts), pedantic -Werror//WX clean.
Scope. No ToolEntry / Rosetta / DSL changes (tools.total stays 367); genome_save/genome_append/genome_catalog tool-schema summaries updated for the v4/regions/O(1) semantics. Tests: +7 (test_genome_o1_append_rc115.py — the v4 shape, O(1)-append invariants, chain re-verifiability, §44 rebuild==written on v4, region↔.chr provenance unification, single-pass pack round-trip); the rc114 back-compat fixture + suite stay green (3 assertions updated where they pinned the v3/whole-body-hash contract: format_version == 3 → 4, the rebuild body_sha256 is now the chain not the v2 whole-body digest). 5 SSOT files rc114 → rc115.
[0.9.0rc114] - 2026-07-03¶
Genome storage format v3 — BIT-PACKED leaves: 4 Klein-4 symbols per byte in turns.bin ("the 2-bit lane IS the format"; issue #1245 ask (a), UPSTREAM §55, F1036/F833). tools.total stays 367 (a format-internal change — no new public op); ABI stays 3 (no exported-symbol wire format changed — the on-disk FILE format is versioned in the manifest, and the C symbols take/return the same body-bytes arguments); numpy stays absent; no abs(); no float.
THE PROBLEM (measured at rc107). Every 2-bit Klein-4 symbol was stored as a FULL BYTE: the issue's reference chromosome (1,024 leaves × 256 symbols = 64 KB of actual payload) wrote 264,230 bytes — a 4.03× bloat that would turn the F1035 3.43 MB quantized kernel into ~36 MB as a genome (>10× worse than the flat gz blob it should replace).
THE FORMAT (v3). A data turn is written on disk as [PACKED_TURN_MARKER (0x51 'Q')] + ceil(leaf_dim/4) payload bytes — symbol i in payload byte i//4 at bit shift 6 − 2*(i%4) (first symbol in the HIGH lanes; a partial final byte's unused low lanes are zero, so the codec is canonical both ways). Cap blocks (CHROM/GENE) keep their §44 leaf_dim-byte inline-label layout. The strand stays SELF-DESCRIBING (§44 held): a block's FIRST byte keys both its kind and its width — 0x43/0x47 → cap, 0x51 → packed turn, 0..3 → legacy v2 byte-per-symbol turn — so ONE walk reads v2, v3, and MIXED bodies alike. Back-compat is STRUCTURAL, not a converter: an old genome needs no migration; an append onto a v2 genome writes a packed region after the legacy blocks and every read handles the mixed body. format_version 2→3 in the manifest; n_turns now counts strand BLOCKS (identical to body_len/leaf_dim on any v2 body, so old manifests stay consistent); body_sha256 semantics unchanged (whole-turns.bin hash); the manifest-less §44 rebuild-by-scan walks all three block kinds.
THE DoD NUMBERS (measured; generating code committed at docs/srmech/notes/rc114_genome_bitpack_bench.py). The 1,024×256 chromosome now writes turns.bin = 66,816 B (256-byte cap + 1,024 × 65-byte packed turns) + manifest 1,821 B = 68,637 B total vs 264,230 B at rc107 — a 3.85× reduction (65,536 B of payload + 3,101 B of self-describing-format overhead: the 1-byte per-turn kind marker IS the §44 scan property). Round-trip stays EXACT leaf-for-leaf (window + recall), including partial-final-byte widths (leaf_dim % 4 ≠ 0).
BACK-COMPAT PROOF (committed fixture). tests/data/genome_v2_fixture/ is an on-disk genome written VERBATIM by the pre-rc114 (rc113) code path — its manifest carries parser_version "srmech 0.9.0rc113" as provenance. The rc114 suite (tests/test_genome_bitpacked_rc114.py, 14 tests) proves: it reads identically (catalog/load/window/genes, leaf-for-leaf against the regenerated strand — reads never rewrite it); a v3 append onto it stays append-only and yields a MIXED body whose every chromosome reads correctly; the §44 manifest-less rebuild reproduces the mixed manifest exactly; in-place edits splice across mixed block kinds; a legacy .chr exports/imports its region VERBATIM (never re-encoded).
Same-rc C mirror (everything-mirrors). The dual-format block walker lands in c/src/srmech_genome.c (genome_block_len + the reworked genome_count_chroms/genome_scan_chroms; SRMECH_GENOME_FORMAT_VERSION 3 + SRMECH_GENOME_PACKED_TURN_MARKER in srmech.h; the parser_rule_hash pre-images track v3), so all 11 srmech_genome_* symbols read/write the new format byte-identically to Python — the rc153 native-vs-forced-pure differential (save/catalog/load/window/append/remove/replace/export/import/explode/pack) passes on the packed format, and the rc114 back-compat tests run THROUGH the native path (native is authoritative when present). The C smoke test extends with a §55 section (mixed packed+legacy body: scan/catalog/load/window/manifest-less rebuild; an unrecognised kind byte is BAD_INPUT). JPL-clean (caller-arena, malloc-free, ≤60-line fns, ≥2 asserts), pedantic -Werror clean BOTH modes (asserts-live and -DNDEBUG).
Issue #1245 ask (b) — O(1)-amortized append + non-quadratic genome_pack — is the DIRECT FOLLOW-UP rc (split per user direction 2026-07-03: ship (a) COMPLETE, (b) the literal next rc — never half of either). The rc114 append baseline over packed bodies, measured for that work: 10/20/40 appends of the 1,024-leaf chromosome at 0.305/0.287/0.291 s/append (pure path; the ~3.9× smaller bodies push the F833 wall out by the same factor, but the per-append full-body rewrite remains and (b) removes it).
Scope. No ToolEntry / Rosetta / DSL changes (format-internal; tools.total stays 367). Tests: +14 (test_genome_bitpacked_rc114.py) + the committed v2 fixture; 3 existing tests updated where they asserted the v2 byte-per-symbol layout (test_genome_multigene_persist_rc142 marker stride → walker; test_genome_in_place_edit_rc146 n_turns/byte arithmetic → block-count arithmetic + packed regions; test_genome_native_dispatch_rc153 big-body sizing → packed-turn sizing). 5 SSOT files rc113 → rc114.
[0.9.0rc113] - 2026-07-03¶
PROSE-SIDE carrier constructors for the q-series row + the FIRST UnaryTheta consumer — the register-chained MOCK-THETA SPARSE-FORM pipeline, opened (issue #1239; siona conversational drive, F1027 / UPSTREAM §85). tools.total 363 → 367 (4 genuinely NEW public ops); ABI stays 3 (NO C code touched — no new symbol needed, see below); numpy stays absent; no abs(); no float.
WHY (the pipeline-opening rationale). Siona's grounded inference loop drives srmech tools from natural utterances (schema-fit binding + a result register that chains RETURNED carriers into the next call). The q-series/mock-theta row was HALF-reachable: unary_theta was fully prose-drivable (the Zwegers/Zagier shadow from one utterance), but (a) the sparse-form engine (q_gosper/q_zeilberger/q_wz_certificate) consumes QPoly/QBiPoly term-ratio carriers that had no constructor tools in the registry — the loop can bind ints/floats/strs/bytes/edge-pairs, but nothing RETURNED a QPoly it could chain; and (b) nothing consumed a UnaryTheta — a conversationally-built shadow was a dead end after display. rc113 closes both gaps.
THE THREE BUILDERS (non_compute; the coupling.from_bodies / text.cooccurrence_edges precedent — they construct operands, every computation lives in the ops that consume them). poly.poly_from_coeffs(coeffs: list[int]) → Poly (ascending-degree integer coefficients; the Σ-row gosper/zeilberger/wz_certificate operands become register-chainable); qpoly.qpoly_from_coeffs(coeffs: list, x_low: int = 0) → QPoly; qbipoly.qbipoly_from_coeffs(coeffs: list[list[int]]) → QBiPoly. The prose cell grammar is deliberately UNAMBIGUOUS: an int leaf is a constant-in-q coefficient; a list of ints is ALWAYS the ascending-q-degree ℚ[q] coefficient ([0, 1] is q) — deliberately UNLIKE the in-process carrier cell coercion, where a 2-int list is a (num, den) rational pair ([0, 1] would be 0). Integer leaves only — the exact-ℚ prose discipline (integers ARE exact); a float/bool/str leaf is an honest TypeError (exact rationals enter via the in-process constructors). This grammar expresses the REAL q-row operands from utterance-expressible lists: the q-binomial-theorem term ratios entirely with integer leaves (e.g. r_n den qX − Y = [[0, [0, 1]], [-1]]), and the order-3 mock-theta term ratio q·x²/(1+q·x)² = [0, 0, [0, 1]] over [1, [0, 2], [0, 0, 1]].
THE UnaryTheta CONSUMER. unary_theta.theta_coefficients(theta: UnaryTheta, n_max: int) → list[int] — the exact integer q-expansion reader (= UnaryTheta.q_series after the leading-power factor-out): θ₃ → [1, 2, 0, 0, 2, …]; the g₃ shadow → the Zagier coefficients [1, −5, −7, 0, 0, 11, 0, 13, …] (Astérisque 326 (2009), Exp. 986, p. 150 — the rc70 SSOT citation; no new external citations). A JSON caller may pass theta='g3' (the existing MCP named-shadow coercer); a register caller chains the UnaryTheta returned by unary_theta. Compute-class judgment: COMPUTE, c_dispatched — through the EXISTING rc70 1:1 C peer srmech_unary_theta (the expansion IS q_series, which routes to that symbol; byte-identical exact-integer mirror; a bare C host calls srmech_unary_theta directly). No new C symbol ships because the 1:1 mirror already ships — a srmech_theta_coefficients would duplicate the identical wire computation (the same-rc mirror rule is satisfied by the existing peer; native-vs-forced-pure parity asserted in the rc113 suite, skip-clean without the lib).
THE CAPSTONE (load-bearing; tests/test_qrow_prose_constructors_rc113.py, 19 tests). "Find the sparse form of a mock theta equation" as a REGISTER-CHAINED pipeline, every step invoked VIA the registry (invoke_tool — the tool entries, not direct imports — proving prose-reachability): (reducible) integer-leaf lists → qbipoly_from_coeffs ×4 → chained verbatim into q_zeilberger → the ORDER-1 certified recurrence (1+qⁿ)f(n) − f(n+1) = 0 for the q-binomial-theorem sum, INDEPENDENTLY verified to annihilate ∏_{i<n}(1+qⁱ) at q ∈ {2,3,5} (never trusting the engine); (honest OPEN) the Euler q-exponential term t(k) = 1/(q;q)_k (ratio 1/(1−q·x)) → q_gosper → None (no q-hypergeometric antidifference — Σ 1/(q;q)_k = 1/(q;q)_∞, Euler; the q-analog of the rc41 harmonic→None case); (the mock theta itself) the order-3 f(q) = Σ q^{k²}/(−q;q)_k² term ratio constructed via the registry from integer leaves, round-tripped EXACTLY against the in-process carrier algebra ((1+qx)² as a genuine QPoly square), and probed at a bounded certificate degree → None (no certificate of y-degree ≤ 5); (the shadow side) unary_theta → theta_coefficients register-chained, exact ints verified against an INDEPENDENT hand computation of the Zagier g₃ coefficients + θ₃. Constructors validate (honest TypeErrors on junk) and round-trip (from_coeffs output == the in-process-constructed carrier, exact ==, the rc56 keystone operands).
HONEST ENGINE BOUNDARY (lodged, not hidden). On the genuine mock-theta ratio the shipped rc55 q_gosper certificate-degree sweep (_MAX_Y_DEGREE = 16) is computationally impractical: the exact-ℚ(q) undetermined-coefficient RREF grows SUPEREXPONENTIALLY in the sweep degree on this input (measured ~0.08 s at cap 4, ~6.4 s at cap 8 → ≫ hours at 16). The rc113 suite therefore states exactly what it proves (no certificate of y-degree ≤ 5, monkeypatch-bounded probe); the mathematics says no certificate exists at ANY degree (mock thetas are genuinely mock modular — Zagier). A per-call degree/effort bound on the q-row engines is a candidate future refinement.
MCP / harness wiring. list[list[int]] added to the MCP type LEXICON as a JSON array (it previously degraded to the string fallback — also fixes the rc44 gf_rref rows schema); its coercer already existed (_identity, rc44). The §10.1 every-tool smoke synth table gains list[list[int]] (a valid nonzero QBiPoly / GF-matrix shape) and UnaryTheta (the named 'g3' shadow — also drives harmonic_maass.shadow with a genuine carrier). The shared return_type_agrees matcher (conftest) gains genuine QPoly / QBiPoly / Poly carrier checks — QPoly/QBiPoly MUST precede the bare Q scalar token (the startswith prefix would mis-route them), and Poly graduates from an unassertable None-skip to a real isinstance check (the immolation return-type-honesty gate now verifies the builders' returns for real).
Rosetta ledger. 4 new rows: poly.poly_from_coeffs / qpoly.qpoly_from_coeffs / qbipoly.qbipoly_from_coeffs → non_compute (builders); unary_theta.theta_coefficients → c_dispatched (through the existing rc70 peer). No debt-bucket change (the down-only python_only_debt ceiling stays 103; c_exists_unbound stays 0).
Scope. 4 new ToolEntries (describe()["tools"]["total"] 363 → 367; the 37 current-total pin statements across 24 test files updated, historical "→ 363" arrows preserved); NO C code touched (ABI stays 3; JPL/pedantic untouched); no DSL descriptor (builders return carriers — the from_bodies precedent); MPM: the q-row ops' existing citations are the SSOT (Koornwinder JCAM 48 (1993) 91–111; Gasper & Rahman; Wilf–Zeilberger Invent. Math. 108 (1992); Zagier Astérisque 326 (2009) p. 150) — no new external citations. 5 SSOT files rc112 → rc113.
[0.9.0rc112] - 2026-07-03¶
cascade.phase_coherent_peak — the LIGHTWEIGHT matched-filter PEAK READ over a rung/mode ladder, kept API-DISTINCT from the full QDFT/ODFT transforms (issue #1234 Item 1d, the F1000→F1001→F1002 refinement — the LAST item of #1234). tools.total 362 → 363 (a genuinely NEW public op — its own ToolEntry); ABI stays 3 (additive symbol only); numpy stays absent; no abs(); no libm.
WHY THIS IS ITS OWN OP, NOT A KWARG ON THE TRANSFORMS (the READ-vs-ENCODE split; F1000 → F1001 → F1002). The full quaternion_dft / octonion_dft (rc110/rc111) are the SPREAD-SPECTRUM ENCODING surface — they compute the whole length-N spectrum. For the RBS-LM READ, the useful reduction is ONLY the phase-coherent PEAK / matched filter over the rung ladder, NOT the full transform: F1000 prototyped the peak read (closing the F995 rung-selection gap, elliptic −z⁻¹ +6pp on the peak); F1001 then built the full complex QDFT and measured it WORSE than the peak on the single-rung fold (independent QDFT-peak 53% → full-QDFT 51%; elliptic 58% → 49% — the +6pp vanished), because the target's cross-rung response is a SPIKE: the peak (max phase-coherent energy) IS the matched filter (it rejects the off-rung noise), while the full transform coherently combines ALL rungs including the off-rung noise (a spike's spectrum is flat by Parseval, so coherent combination gains nothing and forfeits the max's noise-rejection); F1002 settled it read-independently (the elliptic code is perfectly circulant / GENERATIVE but recall-equivalent to independent keys — the transform's value is encoding, not read-amplification). So the READ path wants ONLY this lightweight peak reduction. There is NO twiddle in this op — its absence IS what distinguishes the read from the transform. The rc110/rc111 docstrings + descriptors already pointed here (the read_path_split field).
THE OP (matched filter over a rung ladder). phase_coherent_peak(ladder, *, keys=None). ladder = n_rungs per-rung samples along the rung/mode axis — each a real scalar, a complex phase sample, or a quaternion / octonion / Klein-4 component vector (all the same dimension d; a real (n_rungs, d) Mat is accepted). Per-rung phase-coherent energy: with keys=None (the identity matched filter — F1001's read) E_r = Σ_i ladder[r][i]² (the sample's squared magnitude; for a real-similarity response ladder this IS F1001's max-over-rungs read — the spike's magnitude dominates); with keys given (an explicit expected per-rung pattern, e.g. the −z⁻¹/twiddle ladder) E_r = (Σ_i keys[r][i]·ladder[r][i])² (the squared matched-filter correlation). The PEAK is argmax_r E_r (ties → lowest index). Returns {rung_index, score (the peak's phase-coherent energy = squared magnitude), scores (every rung's energy)} — no sqrt on the decision path, so the read is exact / libm-free. HOW IT MATCHES F1001: F1001's probe scored each candidate by max_r svec(t)[r] where svec is the per-rung response ladder; phase_coherent_peak(svec) is exactly that peak/argmax read (squared-magnitude form — the phase-coherent generalization that coincides with the signed max on the winning positive spike), the thing F1001 proved BEATS the full transform.
Class-K discipline. The per-rung energy is the Class-K real pin-slot squared magnitude (sum of squares — never abs()), and the argmax is a Class-K magnitude comparison (strict >, lowest index on a tie). The phase / sign is a nuisance parameter — magnitude is the correct phase-coherent detector.
Same-rc C peer (everything-mirrors; c/src/srmech_phase_coherent.c). srmech_phase_coherent_peak(ladder, keys, n_rungs, dim, out_index, out_score, out_scores) — a LINEAR matched-filter accumulation (keys==NULL → the identity self-energy filter). The TU carries #pragma STDC FP_CONTRACT OFF for clang (the accumulation loops are under the byte-exact contract; clang defaults contraction ON — the rc110 lesson). Fixed locals only (no malloc, no arena), no libm, JPL-clean (≤60-line functions, ≥2 asserts, no goto / multi-line macros), pedantic-clean BOTH modes (-DNDEBUG and asserts-live, -Werror). New additive symbol → ABI stays 3; the Python op dispatches the whole read in ONE ctypes call when the symbol is present (hasattr-guarded), byte-exact pure fallback otherwise. Python==C parity is BYTE-EXACT (==, not a tolerance): swept native-vs-forced-pure across sample dims {1,2,4,8} × keys/no-keys × several rung counts.
The demonstration test (load-bearing; tests/test_phase_coherent_peak_rc112.py). Reproduces F1001 in miniature: on a single-rung spike (amplitude 2.5σ, NR=6, 300 trials, 1 true + 7 decoy candidates), candidate discrimination by phase_coherent_peak vs by the actual rc110 quaternion_dft bin-argmax (the full transform as the competitor — the real per-rung responses lie in ℝ[i]≅ℂ, so it IS the complex QDFT F1001 measured). Measured: peak 58.5% vs full-QDFT 41.2% (+17.3pp) — the peak read is measurably better, exactly the F1001 result; the mechanism test confirms a unit spike's QDFT spectrum is perfectly flat (|X[k]|²=1 every bin — Parseval), so the full transform cannot localise the rung at all.
Rosetta ledger. New row cascade.phase_coherent_peak → c_dispatched (same-rc C peer). No debt-bucket change (the down-only python_only_debt ceiling stays 103).
Scope. 1 new ToolEntry (describe()["tools"]["total"] 362 → 363; the 29 tools.total / len(shipped) pins updated); the submodule-dotted hypercomplex_dft.phase_coherent_peak added to the test_tool_schema_coverage exempt list (registered flat, exactly like the quaternion_dft/octonion_dft peers); no new MCP param types (sequence params like the transforms); no DSL cascade-catalog descriptor (a terminal READ returning a dict, not a value-flow chain stage — exactly like its module siblings hypercomplex_couple / hypercomplex_exp). MPM: the findings F999–F1002 are the in-repo SSOT (no external citation). 5 SSOT files rc111 → rc112.
[0.9.0rc111] - 2026-07-03¶
cascade.octonion_dft GRADUATED first-class — the OCTONION DFT with the BRACKETING CONVENTION as an explicit ATTESTED field, over a new qm.octonion twiddle family, with the same-rc whole-transform C peer srmech_octonion_dft covering ALL THREE forms (issue #1234 Item 1c, re-raise of #863). tools.total 359 → 362 (3 new qm.octonion twiddle-family ToolEntries; the octonion_dft ToolEntry pre-existed from v0.7.0rc31 — its graduation moves the Rosetta bucket, not the count); ABI stays 3 (additive symbols only); numpy stays absent; no abs(); no libm.
WHY THE BRACKETING IS AN ATTESTED FIELD (F378 — the honesty requirement specific to the octonion case). 𝕆 is NON-ASSOCIATIVE, so "the ODFT" is NOT unique until its bracketing convention is DECLARED — a different bracketing is a DIFFERENT, also-declarable transform. The declaration is now an explicit ATTESTED field (the AMSC discipline applied to a convention rather than a datum): the octonion_dft.toml [cascade.bracketing] block declares, verbatim, convention = "per-summand-single-product; the inverse applies the conjugate twiddle (σ flip) on the SAME declared side; the two-sided 3-factor association order is the explicit bracketing parameter", plus left_associated = "(W_l · x) · W_r" / right_associated = "W_l · (x · W_r)", measurably_differ = true, the why_declared rationale, and the new alternativity_note; the op docstring surfaces the same convention. Tested as load-bearing, not decorative: a deliberately different bracketing yields a DIFFERENT result — the two-sided left/right_associated spectra diverge for distinct axes (max-coefficient difference ~1.55 on the reference signal), and associating the twiddle through a product of two samples (W·(x·y) vs (W·x)·y) diverges by ~12.2 on integer octonions.
THE ALTERNATIVITY FINDING (where non-associativity actually bites — determined precisely + verified empirically over the attested table). 𝕆 is ALTERNATIVE (a(ax) = (aa)x, (xa)a = x(aa) — verified EXACT (==) on integer octonions, alongside the flexible law and a genuine associator witness (e1·e2)·e5 = −e6 ≠ +e6 = e1·(e2·e5)), and by Artin's theorem every 2-generated subalgebra is associative. Consequence: each one-sided summand is ONE product W·x[m] (no association ambiguity), and the whole one-sided round-trip — twiddles on a single axis μ against one sample — lives in the two-generator subalgebra ⟨μ, x[m]⟩, so W̄·(W·x) = (W̄·W)·x = x holds EXACTLY and the same-axis round-trip is exact despite non-associativity (the same guarantee ℍ gives the QDFT; verified: conj(W)·(W·x) recovers x at ~1e-16, round-trips at ~1e-16 across all axes and lengths). Non-associativity bites only at ≥ 3 independent generators: the two-sided form with DISTINCT axes (⟨μ_l, μ_r, x⟩ — the bracketings diverge) and the twiddle-through-sample-product association (⟨μ, x, y⟩). The boundary is demonstrated from BOTH sides: with μ_l = μ_r the two two-sided bracketings COINCIDE (~4e-16 — two generators again), with distinct axes they split (~1.55). The two-sided form stays FORWARD-ONLY (its inverse is open under non-associativity → raises).
THE CONVENTION (mirroring rc110; forward sign σ = −1, W(θ) = exp(μθ)). LEFT form: X[k] = Σ_m W(σ·2πkm/N)·x[m]; RIGHT form: X[k] = Σ_m x[m]·W(σ·2πkm/N); TWO-SIDED: X[k] = Σ_m bracket(W_l, x[m], W_r) keyed by the declared bracketing. The INVERSE (one-sided) flips σ to +1 and scales by 1/N on the SAME side. Round-trips exact both one-sided forms, N ∈ {1,2,3,5,7,8,12,16} (non-powers-of-2 included), every named axis + general axes; LEFT ≠ RIGHT genuinely, with EXACT left==right degeneracy for ℝ[μ] signals; Parseval Σ_k ‖X[k]‖² = N·Σ_m ‖x[m]‖² both one-sided forms (𝕆 is a composition algebra).
Klein-4 ⊗ 2 preservation (the rc110 demo one rung up). A signal carrying sign-structure in the EXTRA octonion axes (bits on e1/e2 — the ℍ Klein-4 pair — AND e4/e6 beyond ℍ, 16 sectors) round-trips through the ODFT with every sector recovered exactly (~1e-16); the same samples are REJECTED by quaternion_dft (not quaternions) and the ℂ projection has no slots for the e4/e6 channels at all (loss exactly 1.0 per channel).
The qm.octonion twiddle family (3 new public ops — the dim-8 mirror of the rc109 quaternion foundation). octonion_exp(theta, mu) = cos θ·1 + sin θ·μ̂ for a UNIT pure imaginary μ̂ — named axes 'i'|'j'|'k' (= 'e1'|'e2'|'e3'), 'e4'..'e7' (the extra octonion axes), 'ijk', 'diagonal' ((Σe_a)/√7), or a general pure-imaginary 4-/8-vector (Class-N sqrt normalisation); octonion_exp_series_truncate(p, q, N, axis) — the EXACT-rational tier, axis ∈ {1..7} (bignum_reference oracle; verified against an independent Fraction Taylor oracle); octonion_twiddle(j, k, N, mu=, sigma=) — the DFT-facing exp(σ·μ·2πjk/N) with the index reduced in Z_N FIRST (Class I) and π entering ONCE as the Class-N 4·atan(1) cascade (never math.pi). Gates mirror rc109 at dim 8: unit norm, same-axis addition law, conjugate-is-inverse, N-th-roots closure, cyclic index reduction, σ-conjugacy, contract errors.
Same-rc C peers (everything-mirrors; c/src/srmech_octonion.c). srmech_octonion_exp + srmech_octonion_twiddle (the rc109 quaternion-peer shape at dim 8: caller-normalised unit μ, Q61 trig projected once, uint64-exact jk mod N, π = 4·atan_q61(1)) + srmech_octonion_dft — the WHOLE O(N²) exact-reference transform, ALL THREE forms + the declared bracketing as an explicit int32 argument, composed over srmech_octonion_twiddle + the existing octonion loop operators srmech_loop_{left,right}_op_f64. The rc110 lesson applied: the TU carries #pragma STDC FP_CONTRACT OFF for clang (the accumulation loops are under the byte-exact contract; clang defaults contraction ON and diverged the macOS cell at rc110). Fixed dim-8 locals only (no malloc, no arena), no libm, JPL-clean (≤60-line functions, ≥2 asserts, no goto / multi-line macros), pedantic-clean BOTH modes (-DNDEBUG and asserts-live, -Werror). New additive symbols → ABI stays 3; the Python op dispatches the whole transform in ONE ctypes call when the symbol is present (hasattr-guarded), byte-exact composed fallback otherwise. Python==C parity is BYTE-EXACT (==, not a tolerance): swept native-vs-forced-pure across both one-sided forms × inverse × 10 axes (incl. e4..e7 + a general axis) × lengths incl. non-powers-of-2, AND both two-sided bracketings (330 case-combinations, 0 mismatches).
The TOML cascade descriptor (octonion_dft.toml) graduated. class_composition M∘C∘N → M∘C∘N∘I; new [cascade.native] (c_symbol/abi_version/abi_note incl. the FP_CONTRACT note); [cascade.graduation] tier prototype → graduated; [cascade.composes] repointed at the qm.octonion twiddle family; mu_axes extended with e1..e7 + diagonal; the [cascade.bracketing] ATTESTED block upgraded with convention + alternativity_note (the Item 1c ask). Loaded + run through srmech.dsl (tested: chain == direct; the attested fields asserted).
MPM citation gate (issue-mandated; docs/srmech/notes/qdft_odft_citation_verification_863.md §rc111). The ODFT anchor — Błaszczyk (2019), arXiv:1905.12631 — was RE-verified first-hand this rc by fetching the OA arXiv PDF (v2, 20 Dec 2019) and extracting its text (pypdf, 31 pages): title + sole author + arXiv ID confirmed; the Hahn & Snopek (2011) origin confirmed in the abstract AND as reference [17] with vol/issue/pages exactly matching the lodged anchor; and — the rc111 load-bearing check — the paper's own OFT definition (Eq 2.8) is immediately followed by the verbatim declaration "Recall that the octonion algebra is non-associative, so it is necessary to note that the multiplication in the above integrals is done from left to right" — i.e. the literature's own ODFT must (and does) declare its association order; srmech's bracketing-as-attested-field is the same honesty requirement made machine-readable. The srmech operational convention + the alternativity/Artin finding remain the IN-REPO SSOT (the attested qm.octonion table + this rc's empirical verification); no new external citation was lodged.
Rosetta ledger. cascade.octonion_dft moves python_only_debt → c_dispatched; the down-only debt ceiling CEIL_PYTHON_ONLY_DEBT 104 → 103 (locked). New rows: qm.octonion.octonion_exp / octonion_twiddle → c_dispatched; octonion_exp_series_truncate → bignum_reference (the intentional exact-rational oracle tier — no debt change).
Tests (tests/test_octonion_dft_rc111.py, 97; the rc31 test_hypercomplex_dft.py contract suite passes unchanged — incl. the two-sided divergence + inverse-raises + empty-input gates). (a) round-trips exact under the declared convention (both forms × 8 lengths × 10 axes); (b) the different-bracketing divergences (two-sided distinct-axes + through-product); © the alternativity findings (exact alternative/flexible laws, the associator witness, the same-axis Artin-exact round-trip composition, the same-axis two-sided coincidence); (d) the e4..e7 Klein-4 ⊗ 2 preservation demo; (e) left≠right + ℝ[μ] degeneracy + Parseval; (f) native symbols present + BYTE-EXACT native-vs-forced-pure parity (one-sided AND two-sided) + forced-pure completeness; (g) DSL chain + the descriptor's attested bracketing block; (h) tools.total 362 + the ledger buckets + the 3 new ToolEntries; (i) the full rc109-mirror twiddle-family gate set; carrier hygiene (real (N,8) Mat in, complex rejected, 4-vector ℍ ⊂ 𝕆 zero-extension preserved). The 22 tools.total pins updated 359 → 362. 5 SSOT files rc110 → rc111.
[0.9.0rc110] - 2026-07-03¶
cascade.quaternion_dft GRADUATED first-class — the QUATERNION DFT over the rc109 qm.quaternion foundation, LEFT/RIGHT form selectable, with the same-rc whole-transform C peer srmech_quaternion_dft (issue #1234 Item 1b, re-raise of #863). tools.total stays 359 (the ToolEntry pre-existed from v0.7.0rc31 — graduation updates the entry + moves its Rosetta bucket, it does not add a tool); ABI stays 3 (additive symbol only); numpy stays absent; no abs(); no libm.
WHY (F380 / the in-repo R21 proof — the collapse-vs-preserve rationale). A Klein-4 object (hdc.klein4_* — the γ₅ & iω₇ Z₂×Z₂ chirality axes) Fourier-analysed with the complex fft must first be PROJECTED to ℂ, which collapses one of its two Z₂ axes — the flat shadow. The exact fix: Q₈/{±1} ≅ Z₂×Z₂ = Klein-4 (the rc109 bridge gates made this live code), so a QUATERNION FT's coefficient algebra (ℍ) matches the object's value algebra and BOTH chirality axes survive. Made a test this rc (test_klein4_preservation_qdft_keeps_both_axes_complex_fft_loses_one): a Klein-4-structured signal (both Z₂ axes carrying information) round-trips through the QDFT with every sector recovered exactly (component error ~1e-16), while the shipped complex-fft path structurally loses the j-channel in full (max loss exactly 1.0 — the channel has no ℂ slot).
THE CONVENTION (documented precisely; forward sign σ = −1, W(θ) = exp(μθ) = cos θ·1 + sin θ·μ̂). LEFT form: X[k] = Σ_n W(σ·2πkn/N)·x[n] (twiddle multiplies on the LEFT); RIGHT form: X[k] = Σ_n x[n]·W(σ·2πkn/N) (twiddle on the RIGHT). The INVERSE flips σ to +1 and scales by 1/N, keeping the twiddle on the SAME side — each form is the exact inverse of its own inverse-transform (the twiddle lives in the commutative ℝ[μ̂] ≅ ℂ subalgebra, so Σ_k W(μ·2πk(n−n′)/N) = N·δ). The two forms are GENUINELY different transforms (ℍ non-commutative), coinciding exactly iff every sample lies in ℝ[μ̂] (tested as EXACT equality — the classic degeneracy). Parseval under this convention (forward unscaled): Σ_k ‖X[k]‖² = N·Σ_n ‖x[n]‖², both one-sided forms (tested). Module-map locks (tested): the left form is a RIGHT ℍ-module map (QDFT_left(x·q) = QDFT_left(x)·q), the right form the mirror; both are ℝ-linear.
API SPLIT (the issue's F1000→F1001 refinement, stated in the docstring + descriptor). This FULL transform is the SPREAD-SPECTRUM ENCODING / analysis surface. The READ path is deliberately a SEPARATE lightweight op — phase_coherent_peak, the NEXT rc — not this one; do not run the full QDFT to read one peak back.
The graduation (Python). quaternion_dft(x, *, form="left", mu_axis="i", inverse=False) keeps its shipped signature + full axis contract ('i'|'j'|'k'|'ijk'|'diagonal' or a general unit pure-imaginary vector; 4-vec or ℍ-valued 8-vec samples; NEW: a real (N,4) Mat is accepted) but now stands on the rc109 foundation instead of slicing the 8×8 octonion embedding: qm.quaternion.quaternion_twiddle (via a new shared resolved-μ̂ core _twiddle_resolved — μ is resolved ONCE per call, the rc109 one-resolution parity contract) + the 4×4 quaternion_left_mult/quaternion_right_mult operator matvec. octonion_dft/hypercomplex_couple remain the composite tier over qm.octonion (the ODFT graduation is a separate later voxel); the shared _dft_core is now octonion-only.
Same-rc C peer (everything-mirrors). srmech_quaternion_dft(x, n_points, left, inverse, mu, n, out) — the WHOLE O(N²) exact-reference transform in one call, composed over the rc109 C symbols (srmech_quaternion_twiddle + srmech_quaternion_{left,right}_mult); an FFT factorisation is honestly future work (the primes.factor/cyclic Cooley-Tukey reindex rung named in the descriptor). Fixed dim-4 locals only (no malloc, no arena needed), no libm (π = 4·atan_q61(1), Q61 trig), JPL-clean (≤60-line function, ≥2 asserts, no goto / multi-line macros), pedantic-clean BOTH modes (-DNDEBUG and asserts-live, -Werror). New additive symbol → ABI stays 3; the Python op dispatches the whole transform in ONE ctypes call when the symbol is present (hasattr-guarded for stale libs), byte-exact composed fallback otherwise. Python==C parity is BYTE-EXACT (==, not a tolerance): the composed path mirrors the C float-op order exactly (twiddle → operator matrix → row-dot left-to-right → accumulate over n → one final scale) — swept native-vs-forced-pure across both forms × inverse × 6 axes × lengths incl. non-powers-of-2.
The TOML cascade descriptor (quaternion_dft.toml) graduated. class_composition M∘C∘N → M∘C∘N∘I (the exact cyclic kn mod N reduction is now explicit); new [cascade.native] (c_symbol/abi_version/abi_note), [cascade.graduation] tier flipped prototype → graduated with the read_path_split field, [cascade.composes] repointed at the rc109 quaternion atoms, mu_axes lists 'diagonal', Parseval stated in the signature. Loaded + run through srmech.dsl (tested: chain().then("quaternion_dft", ...) == the direct call; the descriptor's native/graduation fields asserted).
MPM citation gate (issue-mandated). The QDFT anchor — Sangwine & Ell (2012), Complex and Hypercomplex Discrete Fourier Transforms Based on Matrix Exponential Form of Euler's Formula, Appl. Math. Comput. 219(2):644-655, arXiv:1001.4379 — was RE-verified first-hand this rc by fetching the OA arXiv PDF and extracting its text: title + authors + arXiv ID + venue confirmed, AND the cited convention confirmed present (the exponential-placement discussion "If the exponential were to be placed on the right, …" + §7 "Extension to two-sided DFTs" + the one-sided QDFT references). The precise operational left/right convention lodged here remains the IN-REPO SSOT (rc109 qm.quaternion + the R21 proof), anchored to that verified OA paper for the exp(μθ) matrix-exponential QDFT framework; no new external citation was added.
Rosetta ledger. cascade.quaternion_dft moves python_only_debt → c_dispatched; the down-only debt ceiling CEIL_PYTHON_ONLY_DEBT 105 → 104 (locked). octonion_dft stays python_only_debt honestly (its C peer is the later ODFT voxel).
Tests (tests/test_quaternion_dft_rc110.py; the rc31 test_hypercomplex_dft.py contract suite passes unchanged). (a) round-trips inverse(forward(x))==x AND forward(inverse(x))==x, BOTH forms, N ∈ {1,2,3,5,7,8,12,16} (non-powers-of-2 included) at ~1e-16; (b) LEFT ≠ RIGHT on a generic signal + EXACT left==right degeneracy for ℝ[μ] signals on each named axis; © the Klein-4 preservation demonstration (above) against the SHIPPED complex fft; (d) ℝ-linearity both forms + the two module-map convention locks; (e) Parseval both forms; (f) native symbol presence on native builds + BYTE-EXACT native-vs-forced-pure parity sweep + the forced-pure path decides completely; (g) DSL chain run + descriptor gates; (h) tools.total stays 359 + the ledger bucket assertion; Mat carrier acceptance (real in, complex rejected) + the rc31 contract errors preserved. 5 SSOT files rc109 → rc110.
[0.9.0rc109] - 2026-07-03¶
The qm.quaternion module — 4×4 left/right multiplication operators + the hypercomplex exp(μθ) twiddle: the QDFT/ODFT FOUNDATION (issue #1234 Item 1a, re-raise of #863 BX-⅚/7). tools.total 350 → 359 (9 ToolEntries); ABI stays 3 (additive symbols only); numpy stays absent; no abs(); no libm.
WHY (F380 / the in-repo R21 proof). A Klein-4 object (hdc.klein4_* — the γ₅ & iω₇ Z₂×Z₂ chirality axes) has NO shipped transform respecting both axes; the complex fft projects to ℂ and collapses one Z₂. The exact fix: Q₈/{±1} ≅ Z₂×Z₂ = Klein-4, so a QUATERNION FT's coefficient algebra (ℍ) matches the object's value algebra (R-RBS-LM-R21_klein4_is_quaternion_units_mod_sign.py — the octonion coset table equals hdc.klein4's XOR table). The existing cascade.quaternion_dft/octonion_dft composites slice the top-left 4×4 block of the 8×8 octonion operators; this rc ships the first-class dim-4 foundation the transform rcs will stand on.
qm.quaternion (9 public ops, mirroring the qm.octonion module pattern). quaternion_mult_table() — the (4,4,4) structure constants via the SAME Cayley-Dickson cocycle (cd_basis_product at dim 4), so the table IS the octonion table restricted to e0..e3 by construction (tested); quaternion_table_attestation() — the Class-A MPR self-attestation over the 64 int8 table bytes (same citation chain as octonion: Baez (2002) arXiv:math/0105155 §1 — no new external source); quaternion_left_mult(q) / quaternion_right_mult(q) — the 4×4 real Mat operators L_q (x → q·x) / R_q (x → x·q); quaternion_conjugate / quaternion_norm — the Class-C flip + Class-K∘C norm (never abs()); the twiddle family: quaternion_exp(theta, mu) = cos θ·1 + sin θ·μ̂ for a UNIT pure imaginary μ̂ (named axes 'i'|'j'|'k'|'ijk' exact, general 4-vectors normalised via the Class-N sqrt cascade), quaternion_exp_series_truncate(p, q, N, axis) — the EXACT-rational tier composing the calculus cos/sin_series_truncate (num, den) pairs for a rational angle + a basis axis, and quaternion_twiddle(j, k, N, mu=, sigma=) — the DFT-facing exp(σ·μ·2πjk/N) with the index reduced in Z_N FIRST (Class I) and π entering ONCE as the Class-N 4·atan(1) cascade (never math.pi).
The exactness convention (stated in the module docstring). Three tiers: EXACT/arbitrary-precision = the series-truncate rational path (quaternion_exp_series_truncate; π is not rational, so the exact tier takes a caller-chosen rational angle, e.g. best_rational over the π cascade); EXACT/fixed-width = the already-shipped cascade.hypercomplex_exp Q61 twiddle (k_axes=3, not duplicated); FLOAT64 boundary = quaternion_exp/quaternion_twiddle list[float] + the Mat operators — the SAME boundary every qm.* op uses, with trig via the Q61 cascade (rational.{cos,sin} / native srmech_{cos,sin}_q61) projected exactly once.
Consistency gates (tested, tests/test_qm_quaternion_rc109.py). (a) OCTONION RESTRICTION: the ℍ table == the 𝕆 table on {e0..e3} (octonion products of the first 4 basis elements stay in the span), and quaternion_left/right_mult(q) == the top-left 4×4 block of octonion_left/right_mult(q ⊕ 0₄); (b) the KLEIN-4 BRIDGE (the R21 proof re-run on this module): the signed basis units close into Q₈ (order 8, non-abelian), the quotient Q₈/{±e0} is abelian and its coset table IS hdc.klein4's XOR table (identity relabel; live-checked against hdc.klein4_bind), and quaternion_left_mult's basis-column sign structure has its unique nonzero at row i⊕j; © L/R LAWS: L(p)R(q) = R(q)L(p) (associativity witness), L(pq) = L(p)L(q), R(pq) = R(q)R(p) (anti-homomorphism), L ≠ R for generic q — exact on integer quaternions; (d) TWIDDLE: ‖exp(μθ)‖ = 1, same-axis addition exp(μθ₁)exp(μθ₂) = exp(μ(θ₁+θ₂)), conj(exp(μθ)) = exp(−μθ), and the N-th-roots closure exp(μ·2π/N)^N = 1 for several N — the DFT twiddle-closure.
Same-rc C peers (everything-mirrors; the srmech_loopbind.c shape at dim 4). srmech_quaternion_left_mult / srmech_quaternion_right_mult (fill a caller 4×4 row-major double array from the 4-vector; the same static Cayley-Dickson unroll as the octonion loop product, so the restriction consistency holds in C too) + srmech_quaternion_exp (caller-provided UNIT μ — the srmech_hypercomplex_couple_q61 contract; Q61 cos/sin projected once, byte-exact with the pure mirror) + srmech_quaternion_twiddle (uint64-exact jk mod N + π = 4·atan_q61(1); delegates to the exp core). No malloc, no libm, JPL-clean (≤60-line functions, ≥2 asserts, no goto / multi-line macros), pedantic-clean both -DNDEBUG and asserts-live. New additive symbols → ABI stays 3; the Python ops dispatch native when present (pure Python the complete alternative), hasattr-guarded for stale libs. Python==C parity: byte-exact per op (forced-pure vs active-path equality on value grids).
Scope. 9 new Rosetta rows (4 c_dispatched: left/right mult + exp + twiddle; 3 composition_of_c: table/conjugate/norm — the octonion precedent; 1 non_compute: the attestation; 1 bignum_reference: the series-truncate tier — no debt-ceiling change); describe()["tools"]["total"] 350 → 359 (the 14 describe() pins + 6 len(shipped) pins updated); no new MCP param types (HV/float/int/str all have coercers). 5 SSOT files rc108 → rc109.
[0.9.0rc108] - 2026-07-02¶
The spectral theta / heat trace of a Laplacian — laplacian.heat_trace(L, t) + the F1007 shadow reader laplacian.ground_state_flux_response(...), Class-L composites with same-rc 1:1 C peers (issue #1234 Item 2 / F1007). tools.total 348 → 350; ABI stays 3 (additive symbols only); numpy stays absent; no abs().
WHY (F1007). The heat trace Θ(t) = Tr(e^{−tL}) = Σₖ e^{−t·λₖ} IS a theta function of the Laplacian (on a cycle, the Jacobi-θ family over the closed-form cyclic spectrum) — the natural READ-INDEPENDENT spectral summary (eigenvalue multiset only; no read basis). F1007 found it carries a clean mock-theta split under magnetic flux: the full trace is flux-invariant (Poisson → the modular/holomorphic part) while the flux shadow lives only in the ground state λ_min(Φ) (0 → positive as Φ: 0 → ½ turn; periodic in integer flux — integer holonomy is gauge-equivalent to none). Overtone (trace) / undertone (ground state) = the holomorphic + shadow split, the same asymmetric-beat family as the elliptic −z⁻¹ arc (F999–F1002). Before this rc there was NO heat-trace op in srmech.amsc.laplacian — F1007 computed it by hand (sum(exp(-t*λ) for λ in jacobi_eigvals(L))).
heat_trace(L, t). Accepts the same L forms the eigensolve ops accept (Mat / list-of-rows / ndarray-like; symmetry/Hermiticity the caller's responsibility) and dispatches real-symmetric → jacobi_eigvals, complex-Hermitian → hermitian_eigendecompose. t is a scalar (→ float) OR a sequence of times (→ real Vec, one Θ per t) — the cheap multi-t generalization: ONE eigensolve serves every t. Exp convention (stated): a float64-carrier Class-L composite like the existing eigensolve ops — the eigensolve is the FPU float algorithm and the exp is the Class-N Q61 cascade (rational.exp pure / srmech_exp native, libm-free) at the spectral-summary boundary; Θ is a spectral SUMMARY, not an exact decision (no float transcendental on any exact decision path).
ground_state_flux_response(n, edges, weights=None, *, fluxes, charges=None). The λ_min(Φ) reader, thin + composable with the rc105 charges= chiral surface: per flux Φ (turns) every edge k gets charge Φ·charges[k] (the per-edge PATTERN, validated parallel to edges; default = the UNIFORM 1/n_edges pattern so a single cycle's total holonomy is exactly Φ turns — the F1007 convention), then magnetic_laplacian(..., charges=scaled) + hermitian_eigendecompose → eigvals[0]. fluxes scalar → float, sequence → real Vec.
Same-rc C peers (everything-mirrors; the srmech_resonant_spectrum composite model). srmech_heat_trace (+ srmech_heat_trace_arena_bytes) composes srmech_jacobi_eigvals (real; sorted ascending to match the Python sum order) / srmech_hermitian_eigendecompose_ws (Hermitian) + srmech_exp (the Q61 libm-free exp) per term. srmech_ground_state_flux_response (+ _arena_bytes) composes srmech_graph_magnetic_laplacian (per-edge chiral mode) + srmech_hermitian_eigendecompose_ws per flux. Both are caller-arena bump-carved (no malloc), JPL-clean (≤60-line functions, ≥2 asserts, no goto / multi-line macros), pedantic-clean both -DNDEBUG and asserts-live. New additive symbols → ABI stays 3; the Python ops dispatch native when the symbols are present (pure Python the complete alternative), hasattr-guarded for stale ABI-3 libs.
KERNEL BUG FIX (pre-existing, FOUND BY this rc's parity gate): srmech_hermitian_eigendecompose_ws could not diagonalise a GENERIC complex Hermitian matrix. The complex-Jacobi rotation in srmech_laplacian.c used the NON-conjugate phase e^{iφ} = γ/|γ| while its pair-update applies H → M·H·Mᴴ with M = [[c, −s·e^{−iφ}], [s·e^{+iφ}, c]] — under THAT transform the pq element becomes a mix of e^{iφ} and e^{−3iφ} terms that NO real rotation angle can annihilate, so the sweep stalled and the kernel returned SRMECH_ERR_OVERFLOW for every genuinely complex input (a 2×2 [[2, 0.3+0.4i], [0.3−0.4i, 1]] never converged). It was invisible because (a) real input (γ_im = 0) and the zero-diagonal pure-imaginary case (σ_y) — the two coincidences that DO work — cover the common test fixtures, and (b) every Python consumer (mat_hermitian_eigendecompose → the QM stack, fiedler_vector, rc105's magnetic PSD checks) silently fell back to the pure-Python cyclic Jacobi (which has the correct convention) on non-OK, so results stayed CORRECT and only the native speed was lost. The rc108 heat-trace parity gate drove the kernel directly on the F1007 magnetic-cycle fixture and asserted SRMECH_OK — surfacing the stall. Fix: one line — the CONJUGATE phase (sinphi = −γ_im/|γ|), making the pq element e^{iφ}·[cs(a_pp−a_qq) + |γ|(c²−s²)] (one common phase, killed by the standard real-τ rotation). Verified: the generic 2×2 now diagonalises in ONE rotation to the exact (3±√2)/2 spectrum with ‖VDVᴴ−H‖ ≈ 1e−11 and orthonormal V; the magnetic cycles (n = 4/6/12, q ∈ {0.05…0.25}) and the rc105 dual-sense fixture all converge natively; real-input results are unchanged (the φ = 0 path is the same up to ±0.0). Real-symmetric consumers (resonant_spectrum, jacobi paths) are untouched; complex consumers now genuinely run native instead of silently falling back.
Tests (tests/test_heat_trace_rc108.py). (a) DoD: heat_trace == the by-hand Σ e^{−tλ} on BOTH paths (real-symmetric via jacobi_eigvals + rational.exp; Hermitian via hermitian_eigendecompose); (b) the F1007 reproduction on a flux-threaded 12-cycle — full trace flux-invariant to numerical tolerance, λ_min moves 0 → 1−cos(π/12) ≈ 0.0341 as Φ: 0 → 0.5, integer-flux periodicity (λ_min(Φ+1) == λ_min(Φ), gauge equivalence); © the cycle attestation: heat_trace on Cₙ == the theta sum over the CLOSED-FORM cyclic spectrum λ_k = 2(1−cos(2πk/n)) (the Jacobi-θ family form; the exact θ₃ identity is the Poisson/continuum limit, honestly noted); (d) Python==C value parity per op (native-only check; pure is the complete alternative) + forced-pure vs native agreement; (e) rc105 composability — a charged (dual-sense) magnetic L feeds heat_trace directly, and an explicit uniform charges= pattern reproduces the default; (f) contract errors (n<1, charges-length mismatch, empty/non-finite t/fluxes) + the registered ToolEntries (tools.total == 350).
[0.9.0rc107] - 2026-07-02¶
SAFE-REGION PUSH-DOWN for the genus-axis theta gate internals (×48…×6,900 measured, bit-identical) + the same-rc C gate kernel — the #707 dive's Deliverable B1 (the deep carrier win). tools.total stays 348 (gate INTERNALS, not new ops); ABI stays 3 (additive symbols only); numpy stays absent; no abs() (the safe-support condition dc·u² ≤ safe is sign-free by construction; magnitudes are Class-K sign branches).
The finding (the #707 measured profile). Every *_holds / *_is_distinct_* gate on the RiemannTheta/G3/G4/G5 carriers compares the two sides of a theta identity ONLY on the safe inner region {Aᵢ ≤ safe, |C_ij| ≤ safe} — but the dense path enumerated the FULL (2·box+1)^g factor lattices, convolved them fully, THEN restricted. Since the diagonal exponents are non-negative and ADDITIVE under the lattice convolution (the carrier's own _diag_restrict soundness argument, already shipped on the g4 Göpel path), a product monomial inside the safe region can only come from factor monomials each with Aᵢ ≤ safe — so each factor is enumerated DIRECTLY on its safe support {u : dc·u² ≤ safe}, the EXACT safe region of the INFINITE theta series (box-parameter-free), and convolved with a diagonal-additivity guard. Bit-identical to the dense path on the compared region — measured (#707 prototypes, pure path): g4 addition ×6,900 (137.8 s → 0.020 s); g2 Göpel ×1,555–×2,964; g3 addition ×537; g5 duplication ×170; g4 Göpel ×137; g4/g3 addition-distinctness ×360/×48 (every ≠-witness verified to live INSIDE the safe region, so the distinctness verdicts are unchanged).
One generic mechanism (Python). Module-level in riemann_theta.py: _sparse_factor (the sparse safe-support factor enumerator, Class-K sign), _sparse_conv (the diagonal-guarded convolution; crosses=False is the diag-only _diag_restrict comparison mode of the distinctness gates), _sparse_product / _sparse_sum (2- and 4-factor signed product sums), _sparse_decide (the comparison decider), plus the per-identity spec builders (_spec_addition / _spec_duplication / _spec_goepel_product / _spec_null_square_omega). ALL sixteen gate bodies across g2/g3/g4/g5 now run these internals; gate SIGNATURES, semantics, box validation, and verdicts are unchanged, and the public dense surfaces (lattice(box), addition_lhs/rhs, duplication_lhs/rhs, goepel_lhs/rhs, the eighth-nome builders) are UNTOUCHED — the existing Python↔C parity tests run unmodified.
Same-rc C gate kernel (everything-mirrors — the un-mirrored convolution bodies were BOTH the parity debt AND the CI cost). ONE generic additive symbol pair covers every gate: srmech_riemann_theta_gate_decide (+ srmech_riemann_theta_gate_count) — genus-parameterized (g ∈ {2..5}), taking the gate's comparison list as an int32 spec built by the Python side (the single SSOT of the pair/syzygy data) and returning per-comparison (equal, lhs_has_genus_cross) verdict ints. Caller-arena (a main hash-table accumulator + four aux tables; per-genus compiled caps per the GOEPEL_CAP precedent; a cap overflow returns SRMECH_ERR_OVERFLOW and the gate falls to the pure sparse body), malloc-free, JPL-clean (≤60-line functions, ≥2 asserts, no goto / multi-line macros), pedantic-clean under -Werror -Wall -Wextra -Wpedantic both -DNDEBUG and asserts-live. Python==C parity asserted per gate spec (both restriction modes, all genera): the C kernel's verdict == the sparse pure verdict == the old dense verdict.
The dispatch-shape change (the rc106 marshaling finding applied). rc106 measured that ctypes dict-marshaling of the eighth-nome lattices inside the gates was a NET SLOWDOWN vs pure at these sizes. The gates now dispatch the WHOLE DECISION to C in ONE call per gate (only verdict ints cross the ctypes boundary) when the rc107 kernel is loaded, else run the pure sparse body — the per-lattice native dispatch inside the gates is BYPASSED (the sparse gate bodies never touch the dense per-lattice enumerators at all; those keep their own dispatch on the public surfaces). The g3/g4 goepel_holds keep their rc78/rc85 whole-decision peers (srmech_riemann_theta_g3_goepel / _g4_goepel) as the attested fallback rung in the chain (rc107 kernel → old kernel → pure sparse).
The no-shell gates (bit-identity tests). New tests/test_riemann_theta_rc107_sparse_gates.py: for EVERY gate (all genera), the sparse side is compared against the OLD dense path's side restricted to the safe region — exact dict equality, not approximate — through the untouched public dense surfaces; the distinctness gates additionally assert verdict identity per comparison (the ≠-witness inside the region) and the g3/g4 diag-only mode is asserted bit-identical to the dense _diag_restrict lattices. A fast subset runs per-CI (~40 s); the FULL sweep at every densely-feasible shipped box (the dense g4 addition side at its shipped box 2 is the 137.8 s headline) is gated behind SRMECH_THETA_SPARSE_FULL=1 and was run green locally (40 passed, 573 s — the sweep's cost IS the dense-side recomputation it compares against). THE HONEST DENSE-FEASIBILITY BOUNDARY: the g3-addition box-6 / g4-addition box-3 / g5-duplication box-2 DEFAULTS are infeasible-dense (a single dense g3 box-6 rhs is ~65 s + 12 million keys PER PAIR — exactly why the push-down was built; no test ever ran them densely); their verdicts are covered sparse-native == sparse-pure. Forced-pure verdicts (the rc106 sentinel fixture) prove the pure sparse bodies alone decide every gate; native parity tests pin the C kernel to the pure decide.
Measured (native gcc build, WSL, same machine, whole-file wall seconds rc106 → rc107): rc85 184.9 → 10.3; rc74 167.8 → 54.4; rc77 132.1 → 10.9; rc78 68.5 → 14.1; rc81 41.1 → 39.2; rc80 29.1 → 16.7; rc86 25.9 → 9.1; rc73 11.8 → 5.4; rc88 9.2 → 9.3; rc75 8.5 → 5.3; rc87 6.0 → 5.7; rc72 5.8 → 5.3; rc76 5.0 → 4.5. Theta family total 695.7 s → 190.2 s (−505 s ≈ 8.4 CI minutes per cell), plus the new 43 s bit-identity file. The residuals are NON-gate content by design (rc81 = the Schottky-form counting kernels; rc74 = direct dense-surface assertions on the public goepel_lhs/rhs — the untouched public surfaces). Every gate at its maximum shipped box now decides in < 0.3 s native / < 2 s pure (all 24 gate/box combinations together: 1.5 s native, 4.5 s pure — was ~950 s of dense gate time). The whole family also passes green in the TRUE forced-pure regime (clean venv, no .so, HAS_NATIVE=False).
[0.9.0rc106] - 2026-07-02¶
Theta-suite TEST de-duplication — kill the literal gate re-runs + make the misleading "pure_python_alone" tests honest (test-only; the #707 sparsification-dive profile findings). ZERO carrier / C-source change: the diff touches only tests/ (+ the version SSOT + this changelog). tools.total stays 348; ABI stays 3; numpy stays absent.
The measured problem (the #707 gate-level profile). The genus-axis theta test files (rc72–rc86) re-ran the EXPENSIVE dense q-series convolution gates identically: (a) every test_pure_python*alone* test re-ran the SAME dispatched gates under a pure-sounding name — it contained NO monkeypatch, so it never forced the pure path (on a native host it re-exercised the C-dispatched path a second time; on a no-C host it duplicated the primary gate byte-for-byte) — rc85's copy alone re-ran RiemannThetaG4.addition_holds(2) + goepel_holds(2) (≈213 s of pure convolution re-run, the single largest line in the family profile); (b) the g4 duplication sides were convolved 3× inside rc80; © FOUR files (rc75/76/77/80) re-ran the identical genus-2 gate set (duplication_holds(6) / addition_holds(8) / goepel_holds(5) / the *_distinct companions) as "no-regression" — every one already executed by its home file's PRIMARY gates in the same suite run.
The fixes (honesty first — no assertion weakened, no primary gate box touched).
- NEW tests/conftest.py helper riemann_theta_force_pure(mp) + function-scoped pure_riemann_theta fixture: every has_native_riemann_theta* availability gate is monkeypatched False AND every riemann_theta*_c native binding is replaced by a record-and-raise sentinel — the pure-alone tests now PROVE the COMPLETE pure body alone ran (any native hit fails the test loudly; the fixture teardown re-asserts zero sentinel hits).
- Every misleading pure_python*alone* test (rc72/73/74/75/77/78/80/85/86) now runs FORCED-pure — at the gate's minimal honest window where the pure cost was prohibitive (rc74: göpel box 5→4 + distinct 5→4; rc77: addition box 3→2 — each smaller box is the gate's own validated minimum and the same file's primary gates still run the larger boxes dispatched). rc76/rc81's pure-alone tests were ALREADY honest (they call the _py oracle bodies directly) and are untouched.
- rc85: the pure-alone re-run of addition_holds(2)+goepel_holds(2) is replaced by a genuine forced-pure exercise: goepel_holds(2) runs the COMPLETE pure decision body (the native srmech_riemann_theta_g4_goepel peer otherwise replaces the WHOLE decision on a native host — this is the one gate whose pure body a native run never touches), plus the pure eighth-nome fallback builders (Ω and 2Ω) proven against the file's own independent _pure_eighth oracle at the gate box. The 213 s convolution re-run is dropped: the g4 addition DECISION body (convolution + safe-region compare) has NO native peer — it is one shared always-pure body already executed by the primary test_addition_identity_holds_exact — and its only native-divergent component (the eighth-nome lattice source) is exactly what the parity tests + the new forced-pure oracle checks cover.
- rc80: the g4 duplication sides at the gate box are convolved ONCE in a module-scope FORCED-pure fixture (g4_dup_sides_pure) shared by the safe-region test + the pure-alone test (3× → 2×; the primary duplication_holds(2) gate keeps its own untouched run; the lattice-source swap costs no coverage — test_python_c_parity_all_characteristics proves native==pure for all 256 characteristics over boxes 0–3).
- rc75/76/77/80: the genus-2 no-regression re-runs reduce to CHEAP structural checks (collapse chains to the θ₃ q-series, the symbolic Rosenhain map, even/odd-null counts, Sp(4,ℤ) transform parity); the dense convolution re-runs drop — each dropped gate is covered by its home file's PRIMARY gates in the same suite run: duplication_holds(4/6/8) = rc72, addition_holds(4/6/8) + addition_is_distinct(6/8) = rc73, goepel_holds(4/5/6) + goepel_is_distinct(4/5) + Rosenhain = rc74.
Measured (native gcc build, WSL, same machine, whole-file wall seconds before → after): rc85 461.3 → 209.8 (−251.5 — the profiled duplicate was the whole gap); rc80 67.1 → 33.5; rc75 44.7 → 9.6; rc76 32.0 → 8.5; rc77 176.3 → 157.1; rc74 214.9 → 198.3; rc86 33.5 → 23.4; rc73 17.6 → 9.8; rc72 5.3 → 5.1; rc78 80.5 → 83.9 (honesty-only fix, no duplicate to shed). Family total 1133.2 s → 739.0 s: −394 s ≈ 6.6 CI minutes recovered with zero coverage loss. All ten files also verified green with the native library HIDDEN (HAS_NATIVE=False — the pure regime). (The deeper rc85 carrier-side sparsification is the separate rc107 line.)
[0.9.0rc105] - 2026-07-02¶
magnetic_laplacian(..., charges=[...]) per-edge charge — the CHIRAL Laplacian for dual-sense knowledge graphs (issue #1234 Item 3 / F1006 / F1007). WHY (the honesty-driven encoding fix): F1006's is-a / is-not-a knowledge audit showed the real signed_laplacian ANNIHILATES a dual-sense edge — "X is-a Y" (+1) and "X is-not-a Y" (−1) sum to 0, so a genuine dual sense (Brown is a pigment color / is not a spectral color) reads as "balanced" and vanishes. F1007: move the two senses onto the phase circle — is-a = e^{+i·2π·q} and is-not-a = e^{−i·2π·q} are conjugate partners that SURVIVE. The existing magnetic_laplacian had exactly the right Hermitian structure but only a single scalar q for all edges, so a mixed is-a/is-not-a graph could not be encoded.
The op. magnetic_laplacian(n, edges, weights=None, *, q=<unset>, charges=None) gains an optional charges: Optional[Iterable[float]] parallel to edges (validated len(charges) == len(edges)), each entry a per-edge charge in turns (the SAME unit as the scalar q; the phase is 2π·c via the Class-N 4·atan(1) cascade π — no math.pi). Each edge k = (u, v, w, c) accumulates the conjugate Hermitian pair L[u,v] += −(w/2)·e^{+i·2π·c} / L[v,u] += −(w/2)·e^{−i·2π·c} (Hermitian BY CONSTRUCTION; the w/2 matches the scalar mode's (W+Wᵀ)/2 magnitude scale; (u,v,c) ≡ (v,u,−c)), and the real diagonal carries the magnitude degree Σ w/2 (PSD per 2×2 edge blocks). A dual-sense pair (a, +q) + (b, −q) on one (u,v) reads −[(a+b)/2·cos(2πq) + i·(a−b)/2·sin(2πq)] — the symmetric content in the real cosine, the is-a/is-not-a IMBALANCE in the imaginary sine residue (chiral flux, not cancellation). Contract: q and charges are mutually exclusive — passing BOTH raises ValueError (silent ignore would hide a modelling error; a sentinel default keeps q unset → 0.25, so q=None/junk still raises the same TypeError as rc104). charges=None is byte-for-byte the rc28 scalar construction (the pure scalar path is split out verbatim as _magnetic_laplacian_scalar_py).
C peer, SAME rc (the 1:1 mirror — never split). There was NO existing C magnetic symbol (the rc26 comment tracked the builder peer as "the tracked next voxel") — so this rc ships the full standalone-C builder srmech_graph_magnetic_laplacian covering BOTH modes in one symbol (charges == NULL → scalar-q; else per-edge), output 2*n*n interleaved (re, im) doubles. A NEW additive symbol → SRMECH_ABI_VERSION stays 3 (the legacy Python-only surface had no symbol to preserve). The phase runs the srmech Q61 trig cascade (srmech_cos_q61 / srmech_sin_q61; π = 4·atan_q61(1) — no libm, no M_PI), and the Q61→double projection (double)v / 2^61 is an exact power-of-two scale of the round-to-nearest int64 — bit-identical to Python's float(Q(v, 2**61)) — so native == pure EXACTLY (asserted ==, no tolerance, in the parity tests, both modes). No scratch and no node cap (standalone-complete honor): scalar mode stages the directed W in the output's own imaginary slots (the final pass rewrites them); charges mode accumulates the degree in the diagonal real slots. JPL Power-of-Ten clean (every function ≤ 60 lines, ≥ 2 asserts, no goto / malloc / multi-line macro); pedantic-clean under -Werror -Wall -Wextra -Wpedantic, both -DNDEBUG and asserts-live. Python dispatches natively in both modes (hasattr-guarded — an older lib runs the complete pure path).
Scope. A parameter extension to an EXISTING op, not a new op → tools.total stays 348 (the ToolEntry summary/parameters updated to carry the charges contract). New test test_magnetic_laplacian_charges_rc105.py: the F1006-style audit fixture (signed annihilation == 0.0 vs magnetic charges survival, the exact −(a−b)/2·sin(2πq) imaginary residue, Hermiticity, PSD spectrum via the Hermitian eigensolver, integer-flux periodicity ≈, the mutual-exclusion / length-validation / TypeError contracts, and pure==native exact parity in both modes). Existing test_directed_signed_laplacian.py green unchanged. numpy-free; no abs().
[0.9.0rc104] - 2026-07-02¶
hdc.klein4_bundle accepts the bundle(Sequence)-style single-list call form — HV-carrier parity across the three core Klein-4 ops (issue #1234 Item 4 / F1005 / UPSTREAM §82). hdc.klein4_bind(a, b) and hdc.klein4_similarity(a, b) already take HV wrapper objects (type(hdc.klein4_random(...))) directly — both route each operand through _as_klein4_buf, whose src = v.buffer if isinstance(v, HV) else v unwrap has always coerced an HV. But the natural bundle call hdc.klein4_bundle([hv1, hv2, …]) — mirroring the base hdc.bundle(vectors: Sequence[bytes]) list API — failed: klein4_bundle(*vectors) is VARARGS, so a single list was captured as ONE *vectors element and handed WHOLE to _as_klein4_buf, which then tried int(hv) and raised ValueError: klein-4 vector must be a 1-D sequence of ints. So the natural output of klein4_random / klein4_bind could not be bundled without unpacking, while the SAME HVs bind/compare fine. (The varargs form klein4_bundle(hv1, hv2) already worked — each HV coerced individually — but the natural list form did not.)
The fix — additive input normalization at the marshaling boundary, NO new coercion. klein4_bundle now also accepts the single-sequence form: a lone list / tuple whose FIRST element is itself a vector container (HV / bytes / bytearray / array / list / tuple) is unwrapped to the vectors sequence, so each vector then rides the SAME _as_klein4_buf HV-coercion that klein4_bind / klein4_similarity use (the mechanism is mirrored, not reinvented). A lone one-vector-of-ints (klein4_bundle([0, 1, 2, 3]) — first element a scalar int) still stays ONE vector, so every pre-existing call form (varargs of vectors, a bare single HV / bytes / array vector, a single int-list vector) is byte-for-byte unchanged. Mixed lists ([hv1, plain_int_list2]) work; the sectors= / parallel= / mode= (chunk / chirality) flags are all bit-identical on HV inputs vs .tolist()-ed inputs.
Scope. Python-side coercion fix ONLY (the marshaling boundary — the carrier/adapter exception to the 1:1 C mirror): the C srmech_klein4_bundle kernel takes the finished array('B') buffers and is UNCHANGED → no c/ change, ABI stays 3, and it is a coercion fix, not a new op → tools.total stays 348. New test test_klein4_bundle_hv_rc104.py (DoD + HV==.tolist() byte-identical across all flag variants + mixed lists + the pre-existing-form regressions). numpy-free (verified in a numpy-absent venv); no abs().
Fix (same rc, native-path verify catch) — the equal-length check now runs BEFORE the native dispatch, closing a pre-existing pure↔native divergence. The pure bundle core has always raised ValueError on unequal vector lengths — but the NATIVE fast path marshals ctypes buffers, which only error on a too-small buffer: an OVERSIZED vector (e.g. klein4_bundle([16-vec, 32-vec]), the first vector setting D=16) was silently truncated to the expected length on the native path while the pure path raised. Pre-existing (the varargs form had the same divergence), surfaced by the rc104 test's mismatch regression running on a native build. The equal-length check (the pure core's own error, verbatim) is now hoisted above the dispatch, so pure and native reject every mismatch identically — undersized AND oversized, both call forms. Silent truncation is a correctness trap, never a convenience.
[0.9.0rc103] - 2026-07-02¶
The CHIRALITY-PRESERVING native PARALLEL fan-out for the interpolation is_zero — the FIRST realization of the general parallel_independent_dispatch pattern. New additive symbol srmech_thetasum_is_zero_interpolation_parallel(...) (in the rc99 srmech_thetasum_interp.c TU): it BFS-peels the top branching levels of the exact structural elliptic-interpolation tree into independent sub-problems, runs the UNCHANGED sequential ti_decide DFS on each over a flat PAL worker pool (srmech_plat_thread_*; deeper recursion stays serial), and AND-folds the per-task verdicts with a best-effort cancel flag (first False short-circuits, preserving the serial early-exit). A bit-identical serial fallback runs when the PAL has no threads OR n_workers <= 1. It is an ACCELERATOR: the Python sequential peer stays the oracle (GIL — no Python parallel peer); is_zero opts in via SRMECH_THETASUM_PARALLEL_ISZERO, and the default dispatch is unchanged (parallel → sequential → pure, each byte-for-byte the same verdict).
Malloc-free W-slice arena (the crux). The DFS arena is bounded by PATH depth (≤ #vars), NOT tree size — so parallelism needs only W independent arena slices, not one per task. The caller ws is carved into [control band | shared-root region | W disjoint worker slices]; the root is parsed ONCE into the shared read-only region (each worker REPLAYS its path from there — no per-task re-parse), each worker owns one disjoint slice reset between tasks (klein4's disjoint-slice race-freedom argument; zero cross-worker writes). All fixed-size, no malloc, JPL-clean.
TWO first-class bit-identical CONTRACTS (both tested as BLOCKERS). (1) CARRIER-PRESERVATION (exact-ℚ, no float, no abs()): the parallel verdict is BYTE-FOR-BYTE the sequential srmech_thetasum_is_zero_interpolation verdict — proven on the Weierstrass three-term (True), the Warnaar Cₙ Lemma 2.2 completion (True), perturbations (False), single-term products (False), the rc102 θ(x⁴)·θ(x⁻⁴) case (False), and a ≥300-case randomized fuzz. (2) CHIRALITY-PRESERVATION: the interpolation ±-pairs are the two chiral halves (over/under x ↔ 1/x); the fan-out is ORDER-FREE — the verdict is INVARIANT to the task enumeration/scheduling order (task_order 0 forward vs 1 reverse) AND to the worker count, favoring neither chirality (mirrors klein4's symmetric-over-sectors guarantee + is_elliptic's x → x⁻¹ relabel invariance).
Speedup — honestly framed. On a SYNTHETIC wide+deep is_zero tree (the three-term identity over three degree-8 variables, ~9³ leaves, fully explored) the fan-out delivers a real, worker-scaling speedup while staying bit-identical: ~1.9× (nw=4) / ~2.4× (nw=8) on a mid-size case, and 2.1× / 2.9× / 3.5× (nw=2/4/8) on the ~65 s all-four-squared monster. Production is_zero value is LIMITED (real inputs are either < 0.5 s or wall'd, and expense concentrates in single base-case grids, not wide independent branching — dive A): the win is the reusable, correctness-guaranteed parallel_independent_dispatch pattern + the two guardrails, not raw is_zero throughput. It does NOT lift the fundamental n=2/N=2 elliptic-Jackson wall (the documented frontier; rc101's _VERIFY_MAX_PARTITIONS cap is unchanged) — CRT-QMat / zeilberger acceleration are the value-realizing follow-ons.
Scope. Accelerator, not a new op → tools.total stays 348; new additive symbols (srmech_thetasum_is_zero_interpolation_parallel + its ..._parallel_ws_bound sizer) → ABI stays 3. ti_decide gained a start_offset parameter (the sequential entry passes 0 — byte-for-byte unchanged; the parallel peer resumes a replayed subtree's augment-prime offset so every prime on a root→leaf path stays globally distinct). test_thetasum_is_zero_parallel_rc103.py; no regression on the rc99/rc98/rc93/f929 suites. JPL Power-of-Ten clean (each function ≤ 60 lines, ≥ 2 asserts, no goto / malloc / multi-line macro); pedantic-build clean under -Werror -Wall -Wextra -Wpedantic -DNDEBUG. numpy-free; no abs().
Fix (same rc) — right-size the new parallel is_zero TEST + a production-only parallel-arena budget (_native.py + the test, Python-only; no C / ABI / tools.total change). The deterministic ubuntu·py3.10 CI failure was an OOM introduced by THIS rc's new code: test_thetasum_is_zero_parallel_rc103.py exercised the parallel peer at up to n_workers=8 on a degree-8 speedup case plus a 340-iteration fuzz, and the parallel arena grows like (n_workers+1)·1.5·ws_bound2 — up to ~1.1 GB per call — so the suite peaked at ~5.4 GB RSS, tipping the memory-constrained py3.10 cell into OOM (the py3.12 cells, with more RAM, fit and passed). The SEQUENTIAL is_zero path is UNCHANGED by rc103 and is NOT the regression (it is untouched). The fix is to right-size the test's inputs — the CI-honest way, shrinking input SIZE, never weakening a contract: cap n_workers ≤ 4 (_WIDTHS, the fuzz, the speedup demo), shrink the deep even-θ fixture x⁴→x³ (the full x⁴ sequential-sizing regression stays covered by test_thetasum_interpolation_rc98/_rc99), cap the fuzz products to ≤ 2 factors, and gc.collect() between fuzz iterations. Every fixture is now sized so the parallel peer DECIDES within a bounded arena, and the carrier + chirality assertions are STRENGTHENED to require a real decided verdict at every width — a None decline now FAILS the test, so it can never "pass" by declining. Peak RSS 5.4 GB → ~2.4 GB on py3.10 (10× loop clean, no hang; a stdlib faulthandler per-test hard timeout makes any future hang a clean FAIL). Additionally, as a PRODUCTION/edge default so the new accelerator never grabs gigs on a real host, the parallel marshaler clamps n_workers to a memory budget (_iszero_ws_budget_bytes, default 256 MiB, override SRMECH_ISZERO_WS_BUDGET_MB), declining to the untouched sequential peer only if even a 2-worker arena is over budget — this is production behavior ONLY and is never the mechanism by which a test passes. Python-only: no C change → ABI stays 3, tools.total stays 348; sequential path + rc101 verify untouched; numpy-free; no abs(). (Separately noted for follow-up, NOT a regression and NOT skipped: the SEQUENTIAL is_zero for the hardest Frenkel–Turaev ₁₀E₉ verify residual (n=1,N=3) sizes to ~19 GB — it passes on the CI runner but MemoryErrors under ~7 GB; reducing that op's true memory is a separate task.)
Finisher (same rc) — the "INFORM, don't LIMIT" RAM-cost approximation + the deterministic informed-skip for the pre-existing ~19 GB sequential verify (Python-only; no C / ABI / tools.total change). High-RAM operations should be known by an approximation so a memory-constrained (edge) caller can tell what is and is not holdable on its hardware — it is not srmech's place to LIMIT the op, only to INFORM the caller. New carrier method ThetaSum.is_zero_ws_estimate_bytes() (+ the _native.thetasum_is_zero_ws_estimate_bytes marshaler): the ESTIMATED sequential is_zero interpolation arena in bytes, computed WITHOUT allocating it — it reuses the rc102 degree-aware C sizer ..._ws_bound2 with the IDENTICAL sizing derivation the real peer uses, so the estimate equals what the peer would allocate (no new C op; a carrier accessor → tools.total stays 348). The op itself is NEVER capped by the estimate — is_zero runs wherever the caller's arena fits (the caller-arena design; the caller now has the number to decide). The pre-existing heavy verify tests ((n=1,N=3) Frenkel–Turaev declares ≈ 18.8 GB) now take a transparent INFORMED-SKIP decided DETERMINISTICALLY UP FRONT — the declared estimate vs the runner's available RAM (MemAvailable / GlobalMemoryStatusEx / sysconf) — because relying on catching MemoryError is a platform lottery: Windows eager-commits (the allocation raises, catchable — this run's windows cell went green exactly that way), but Linux/macOS overcommit — the ~19 GB allocation "succeeds" lazily and the OOM killer SIGKILLs mid-decision with nothing to catch (the flaky ~8-minute py3.10 cancels; the intermittent "passes" were sparse-page-touch luck, not capacity). With the deterministic check, every standard CI cell (7–16 GB) SKIPS the (n=1,N=3) verify with the declared cost + the cell's available RAM in the reason — a skip is not a pass, the verified is True assertion is untouched and runs wherever a ≥19 GB arena genuinely fits, the feasible cases keep verifying True in CI, and the constructive closed form stays MPM-verified at build. Reducing the op's TRUE memory (so the estimate itself shrinks and the verify returns to CI) is the tracked relationship-encoding follow-up. numpy-free; no abs().
[0.9.0rc102] - 2026-07-02¶
Correctness fix for the rc99 srmech_thetasum_is_zero_interpolation workspace sizer — the base-case p,w-series band is now sized from the TRUE ti_deg degree (Σe²), not max_abs_exp², so the C peer stops FALSE-DECLINING feasible multi-theta base-case leaves. The single-variable base case (ti_one_var, mirroring the pure-Python _struct_one_var) sizes its p-order band from ti_deg = the max over terms of the SUM of squared THETA-argument exponents in the base variable (k = max(deg−1, 0) + MARGIN). The rc99 srmech_thetasum_is_zero_interpolation_ws_bound sized that same band from max_abs_exp² (the single largest |exponent|, squared) — which for ANY leaf with ≥2 nonzero-exponent thetas in the base variable is an UNDER-estimate (Σe² ≫ max(e²)): ti_take_words returns NULL → SRMECH_ERR_OVERFLOW → the C peer declines → the Python marshaler falls to the pure oracle. (The same max_abs_exp² also OVER-sized k when a canonicalized prefactor carried a large single exponent, tipping large shapes into an outsized arena request.) An under-provisioning gap, not fundamental slowness.
The fix + why a new symbol (ABI stays 3). The correct k needs the theta-only Σe² degree, a quantity distinct from max_abs_exp (which stays load-bearing for the w-band SPAN + prefactor offset and still covers the canonicalized prefactor's exponent). Both are genuinely required, so the sizer gains an argument — shipped as a new additive symbol srmech_thetasum_is_zero_interpolation_ws_bound2(n_syms, n_terms, max_thetas, coeff_limbs, max_abs_exp, max_theta_sq_sum) (adding a symbol does NOT bump ABI — SRMECH_ABI_VERSION stays 3). The legacy 5-arg ..._ws_bound is kept and now delegates to ..._ws_bound2 passing max_abs_exp² as the degree, so a stale ABI-3 caller / lib links + behaves byte-for-byte as before. The Python marshaler (_native.thetasum_is_zero_interpolation_c) computes max_theta_sq_sum from the term set (walking the flat monomial list by term, summing theta-arg e² per variable, skipping the prefactor exactly as ti_deg does) and prefers ..._ws_bound2 when present (hasattr-guarded), else the legacy sizer.
Proof of value. The single-variable ThetaSum θ(x⁴)·θ(x⁻⁴) (two thetas, |e|=4 → Σe²=32 ≫ max(e²)=16; the OLD k=21 < the base case's needed k=34): the rc101 C peer declined (_is_zero_interpolation_c() → None, SRMECH_ERR_OVERFLOW); after the fix it decides natively (False), equal to the pure _is_zero_interpolation(). Full parity + no regression on the rc99/rc98/rc93/f929 suites (test_thetasum_interpolation_c_parity_rc99.py incl. the ≥300-decided randomized fuzz, test_thetasum_c_parity.py, test_thetasum_interpolation_rc98.py, test_thetasum_overflow_fallback_rc93.py, test_thetasum_f929.py).
Scope. Sizing-correctness only — no new op → tools.total stays 348; ABI stays 3 (additive symbol). The rc101 _VERIFY_MAX_PARTITIONS = 4 verify cap is UNCHANGED: the fix removes the false sizing-decline on feasible leaves, but the genuine n=2/N=2 elliptic-Jackson residual is still fundamentally too costly, so the router cap continues to (correctly) gate it to verified=None. JPL Power-of-Ten clean (each function ≤ 60 lines, ≥ 2 asserts, no goto / malloc / multi-line macro); pedantic-build clean under -Werror -Wall -Wextra -Wpedantic. numpy-free; no abs().
[0.9.0rc101] - 2026-07-01¶
multivariate_elliptic_jackson upgrades from CONSTRUCTIVE to per-call VERIFIED — the Cₙ elliptic Jackson reducer now PROVES its closed form, gating the sigma_elliptic_multivar dispatch router. rc96 CONSTRUCTS the closed-form theta-quotient RHS of the balanced Cₙ very-well-poised elliptic Jackson summation (Rosengren, A multivariable elliptic summation formula, arXiv:math/0101073, Thm 2.1 / Eq 5). Now that rc98 (Python) + rc99 (C peer) made ThetaSum.is_zero COMPLETE for the multi-variable elliptic case (the structural elliptic interpolation), the op gains a verify capability that PROVES the reduction per call: multivariate_elliptic_jackson(..., verify=True) returns {"closed_form": <EllRatio>, "verified": True | False | None}. It builds the LHS n-fold Cₙ sum over the partitions Λ_{nN} = {N ≥ λ₁ ≥ … ≥ λₙ ≥ 0} SYMBOLICALLY as a ThetaSum (the exact twin of the rc96 test's NUMERIC _cn_sum oracle — each elliptic theta-Pochhammer (u; q, p)_k = ∏ θ(u·qⁱ) a product of Theta factors; the balancing e = a²q^{N+1}/(bcd·x^{n-1}) an EllMonomial; the q^{λᵢ}·x^{2(i-1)λᵢ} prefactor Class-K), subtracts this closed form, and decides (LHS − RHS).is_zero. The plain call (verify=False, default) returns the bare EllRatio unchanged — full back-compat.
The verified=None HONEST-infeasible contract (not a failure). The proof builds a term per partition (C(N+n, n) terms) and clears to a common denominator, so the residual's per-variable theta-degree — hence the cost of the exact interpolation is_zero — grows with BOTH the rank n (cross-variable root-system coupling) and the ceiling N (λ-range → q/x powers). A term-count cap (_VERIFY_MAX_PARTITIONS = 4, i.e. C(N+n, n) ≤ 4) is checked FIRST, before any build or is_zero call: a sum above it returns verified=None — an honest "not verified: too large to decide in-budget", NEVER hanging — while the constructive closed_form (the MPM-verified Thm 2.1 RHS, proven at build for the whole Theorem 2.1 family) is returned in every case. The cap is the measured feasibility frontier of the rc98/rc99 is_zero: verify=True is fast (< 0.5 s) and returns True for every (n, N) with C(N+n, n) ≤ 4 — the set {(1,1), (1,2), (1,3), (2,1), (3,1)} — and is genuinely INFEASIBLE beyond it (e.g. n=2/N=2, C=6: the residual has interpolation-degree ~74 in q; the rc99 native peer declines on SRMECH_ERR_OVERFLOW and the pure oracle does not finish in > 9 min). The None therefore never withholds the reduction, only the per-call re-proof.
Router gate. dispatch.infer with row="sigma_elliptic_multivar" (or the eight a/b/c/d/x/q/N/n keys) now calls the verify=True path and SURFACES the real verified status in the reduced dict: True (per-call proof), None (build-verified constructive form, beyond the cap), and a False (closed form provably ≠ the sum) routes to honest OPEN. A CONSTRUCTIVE→VERIFIED reducer is the F929 anti-hallucination discipline made executable, one root-system rank above the ₈ω₇ elliptic_wz_certificate.
Scope. The verify capability is a flag on the existing op + a router gate — pure orchestration over the C-backed ThetaSum.is_zero, no new numerical kernel → tools.total stays 348, ABI stays 3. test_multivariate_elliptic_jackson_verified_rc101.py; numpy-free; no abs().
[0.9.0rc100] - 2026-07-01¶
fractal_spectrum — the Ch-2 (quasi-periodic / fractal) DUAL of coupling.resonant_spectrum. Where resonant_spectrum(L) reads a symmetric coupling Laplacian's FLAT eigenspectrum (ONE eigensolve — the §75 / F928 spectral row), fractal_spectrum(R, branches) reads a self-similar lattice's SPECTRAL-DECIMATION structure: the spectrum is the ITERATED PREIMAGE of the renormalization Poly R (the decimation map, fixed point R(0)=0), NOT a flat list. The op IS a new ToolEntry → tools.total 347 → 348. ABI stays 3 — it is PURE orchestration over already-C-backed ops (Poly.derivative / .eval = has_native_poly; Class-N log / best_rational = C-backed) with NO new numerical kernel, so it ships non_compute (no dedicated C peer — the from_bodies / cooccurrence_edges precedent; everything-mirrors is satisfied because every underlying op is already C-mirrored).
THE GROUNDING (F686 / F974). On the Sierpinski gasket, on the NORMALIZED Laplacian the spectral decimation is exactly R(z)=z(5−4z) (measured — Rammal 1984; Fukushima & Shima, Potential Analysis 1 (1992) 1–35; OA-attested via the arXiv:1505.05855 restatement — the paywalled DOIs are motivation-only). From R the op reads: the exact scale R'(0) (the per-level eigenvalue-shrink factor; 5 for the gasket); the fracton (spectral) dimension d_s = 2·log(branches)/log(scale) as a Class-N best_rational anchor (2·log3/log5 ≈ 1.36521); the F974 bit-exact |q|-meter q_octaves_per_level = ceil(log2(scale)) (3; a pure Q-halving loop — no float, no abs()); rung_class="constant" (ONE R iterated is memoryless-geometric / self-similar); and log_period_over_2pi = 1/log(scale) (the discrete-scale-invariance complex-dimension imaginary period; 1/ln5 ≈ 0.6213). The full spectrum is named the honest spectrum_open — the JULIA SET of R (operand-IRREPRESENTABLE: no finite exact carrier decides λ ∈ spectrum; candidate next-theory = complex dynamics of rational maps / spectral-decimation Julia-set theory).
VALIDATED on R(z)=z(5−4z), branches=3 (gasket: scale 5, d_s ≈ 1.36521, q_octaves 3, rung constant, log_period/2π ≈ 0.6213, spectrum_open present) AND a second lattice R(z)=z(3−2z), branches=5 (scale 3, d_s ≈ 2.9299 — a genuinely different fracton dimension), plus input validation (R(0)≠0 / degree < 2 / scale ≤ 1 / branches < 2 reject; a coefficient-sequence R is coerced via Poly.from_coeffs) — test_fractal_spectrum_rc100.py. Placed in srmech.amsc.coupling alongside resonant_spectrum (its Ch-1 dual). Exact-ℚ; numpy-free; no abs(). The "Poly" param rides the existing rc41 _to_poly MCP coercer (no new param type).
[0.9.0rc99] - 2026-07-01¶
The ThetaSum.is_zero structural elliptic-interpolation completion gets its 1:1 native C peer — srmech_thetasum_is_zero_interpolation — discharging the rc98 "owed everything-mirrors backlog." rc98 shipped the COMPLETE multi-variable elliptic decision (ThetaSum._is_zero_interpolation) in pure Python and left its C mirror owed; this rc lands that peer. Unlike the rc63 ±-pair srmech_thetasum_is_zero (the SOUND fast path — complete only for the single-variable ₈ω₇ class), the new peer is a COMPLETE structural mirror whose verdict EQUALS the pure-Python _is_zero_interpolation verdict byte-for-byte — True AND False — so the dispatched is_zero now trusts the native verdict DIRECTLY (no "sound-fast-path only" caveat).
THE MIRROR (Rosengren arXiv:1608.06161v3 Prop 1.6.1 / eq 1.22 + Cor 1.3.5). The C reproduces _structural_is_zero exactly: interpolate in ONE variable at D_v + 1 distinct points (a degree-D_v theta section vanishing at D_v + 1 points is ≡ 0) → a lower-variable is_zero RECURSES, base = the single-variable degree-bound q-expansion; nodes = the θ-factor ZEROS (killing terms via θ(1) = 0) augmented with globally-distinct primes threaded through the recursion (the load-bearing soundness guard against a spurious θ(1) from constant collisions — the same 113-prime pool, same (offset + used) % NPR threading as Python). Because JPL Rule 1 forbids recursion, the C runs the interpolation tree as an explicit arena-mark DFS: each frame owns its combined terms + substitution nodes below a child-mark, and the pool bump-pointer resets to that mark between children, so the live arena is bounded by the PATH (depth ≤ #variables) times the per-level working set, NOT the total tree-node count. Exact-ℚ over srmech_bigint, no q-grid, no float, Class-K sign (never abs()). Malloc-free (JPL Rule 3) — every working monomial / term / node / dense p,w-series cell is carved from the caller arena, over the SHARED srmech_ellbase_* exact-Q monomial + theta-canon kernels (no second copy of the algebra). A too-small arena / coefficient cap trips SRMECH_ERR_OVERFLOW and the Python marshaler declines to the COMPLETE pure oracle (the C peer is the accelerator, the pure path the authority — the rc42 zeilberger precedent).
PARITY. The native verdict EQUALS the pure-Python _is_zero_interpolation on: the Weierstrass three-term identity (True) + a broken variant (False); Warnaar's Cₙ elliptic Lemma 2.2 (Rosengren arXiv:math/0101073 eq (6) — the cross-variable √a obstruction the whole arc exists to decide, True) + perturbations (False); single-term theta products (False — the soundness cases); and a randomized fuzz where the non-None C verdict equals Python on EVERY case (test_thetasum_interpolation_c_parity_rc99.py; the rc63 test_thetasum_c_parity.py contract is tightened from no-false-accept to FULL parity for the interpolation-backed dispatch). The C peer is measurably faster than the pure interpolation on the keystone. JPL Power-of-Ten clean (every function ≤ 60 lines, ≥ 2 asserts, no goto / malloc / multi-line macro), pedantic-build clean under -Werror / -Wall -Wextra -Wpedantic.
Additive symbol → ABI stays 3. is_zero is a method (not a ToolEntry), so tools.total stays 347.
[0.9.0rc98] - 2026-07-01¶
ThetaSum.is_zero is now COMPLETE for the multi-variable elliptic case — the structural elliptic-interpolation completion closes the cross-variable Cₙ decision the ±-pair reduction could not reach. The single-variable ₈ω₇ decision (the quasi-periodicity grouping + Weierstrass three-term reduction) is COMPLETE for the clean ±-pair class, but a shape OUTSIDE it — the cross-variable root-system Cₙ coupling θ(x_i/x_j)·θ(a·x_i·x_j), whose geometric-mean midpoint x_i·√a is not a perfect-square monomial so no pair forms — was left an honest False ("not proved"). This rc makes the pure-Python is_zero the COMPLETE decision by adding the structural elliptic-interpolation completion (ThetaSum._is_zero_interpolation), so the whole multivariable elliptic Σ-row can be PROVEN per call, not just constructed.
THE ALGORITHM (Rosengren, Elliptic Hypergeometric Functions, arXiv:1608.06161v3, Prop 1.6.1 / eq 1.22 elliptic Lagrange interpolation + Cor 1.3.5 the degree/zero-count law). The cleared numerator N is a theta section jointly in its symbols; N ≡ 0 IFF — interpolating in ONE variable v at D_v + 1 distinct points (a degree-D_v theta section vanishing at D_v + 1 distinct points is identically zero) — N vanishes at each node, a LOWER-variable is_zero → RECURSE, base case = the single-variable degree-bound q-expansion. The nodes are the θ-factor ZEROS (monomials in the remaining variables — substituting kills that term via θ(1) = 0) augmented with globally-distinct PRIMES so no two variables collide to a spurious θ(1). Because the recursion substitutes nodes rather than merging ±-pairs, it never forms the √a geometric mean — it dissolves the very obstruction that stalls the three-term reduction. Exact-ℚ, no q-grid, no float, no abs() (Class-K sign).
SOUND + COMPLETE. SOUND — a genuine non-zero product is never proved ≡0 (the augment nodes are distinct integer primes: θ(∏ pᵢ^{eᵢ}) = θ(1) IFF ∏ pᵢ^{eᵢ} = 1 IFF every eᵢ = 0 by unique factorization, and an integer is never a nome power p^k — so the only vanishings are genuine). COMPLETE — the keystone is Warnaar's Cₙ elliptic Lemma 2.2 (Rosengren, A proof of a multivariable elliptic summation formula conjectured by Warnaar, arXiv:math/0101073, eq (6)), decided ≡0 exactly. Validated against an INDEPENDENT eval-truncation oracle: zero soundness disagreements across the randomized identity/non-identity fuzz (test_thetasum_interpolation_rc98.py).
DISPATCH — the SOUND FAST-PATH contract. is_zero trusts a native True (the ±-pair srmech_thetasum_is_zero peer is SOUND — a native True is a genuine proof) and otherwise falls to the now-COMPLETE pure body. The native ±-pair peer is thus the sound fast-path, not the complete decision; the C-parity suite is reframed to the soundness invariant (the native peer NEVER false-accepts — a native True when not ≡0 is the only BLOCKER; a native False where the interpolation proves True is expected). The full interpolation C peer srmech_thetasum_is_zero_interpolation is the owed everything-mirrors backlog (the rc42 zeilberger common-case-C precedent: the fast path is C-backed, the completion's C is a scoped follow-up). Python-only completion → tools.total stays 347, ABI stays 3.
[0.9.0rc97] - 2026-07-01¶
dispatch.infer routes the multivariate (Cₙ) elliptic Jackson Σ sub-row — the elliptic reduction row is now fully wired into the F929 dispatch table. The router recognises a balanced Cₙ elliptic Jackson relationship — by an explicit row="sigma_elliptic_multivar" (also cn_jackson / elliptic_multivar / multivariate_elliptic) tag, OR by the full eight-key set a,b,c,d,x,q,N,n — and routes it to multivariate_elliptic_jackson (rc96), returning the closed-form theta-quotient product (Rosengren Thm 2.1). A CONSTRUCTIVE reduction (the resonant_spectrum precedent): the MPM-verified Thm 2.1 RHS gated per call on valid Cₙ Jackson parameters (N ≥ 1, n ≥ 1, coercible EllMonomial / symbol-name / int params); a malformed payload routes to the honest OPEN. The exact per-call is_zero PROOF of the reduction is the documented multi-variable-is_zero frontier (the n-variable partial-fraction decision), named truthfully in the new sigma_elliptic_multivar OPEN hint; the sigma_elliptic hint is updated to point at this now-shipped Cₙ row (leaving the Aₙ / higher-genus rows as the honest next OPEN). Pure orchestration over the already-C-mirrored multivariate_elliptic_jackson — ships without a dedicated C peer (the from_bodies / cooccurrence_edges non_compute precedent), so tools.total stays 347 and ABI stays 3. Regression-tested (test_dispatch_sigma_elliptic_multivar_rc97.py): detection by tag + key-set, reduction to the closed form (== the direct multivariate_elliptic_jackson output), symbol-name / EllMonomial param coercion, malformed → OPEN, OPEN-hint presence. numpy-free; no abs().
[0.9.0rc96] - 2026-07-01¶
multivariate_elliptic_jackson — the eq-5 Cₙ REDUCER, the CAPSTONE of the multivariable root-system Cₙ elliptic reduction row. rc94 shipped the FOUNDATION (the elliptic Cauchy / Frobenius determinant); rc95 shipped the ENGINE (the elliptic partial-fraction expansion). This rc ships the CAPSTONE they exist to build: the closed-form reducer for the balanced Cₙ very-well-poised elliptic Jackson summation. Where the single-variable ₈ω₇ elliptic Jackson summation reduces to a scalar theta-quotient (the Frenkel–Turaev sum), the Cₙ (root-system) elliptic Jackson summation reduces an n-FOLD sum over partitions to a theta-quotient PRODUCT. The op IS a new ToolEntry → tools.total 346 → 347. ABI stays 3 (additive C symbol). Shipped CO-EQUAL Python + 1:1 native C peer in the SAME rc (the everything-mirrors / never-split discipline).
THE CLOSED FORM. For the parameters a, b, c, d and the base variables x, q over the partitions N ≥ λ₁ ≥ … ≥ λₙ ≥ 0 (Hjalmar Rosengren, A multivariable elliptic summation formula, arXiv:math/0101073 [math.CA], Theorem 2.1, Eq. 5):
Σ_{λ ∈ Λ_{nN}} [ Cₙ very-well-poised summand ]
= (aq, aq/bc, aq/bd, aq/cd; q, x)_{Nⁿ}
/ (aq/b, aq/c, aq/d, aq/bcd; q, x)_{Nⁿ},
(u; q, x)_{Nⁿ} = ∏_{j=1}^n ∏_{i=0}^{N-1} θ(u·x^{1-j}·qⁱ; p).
multivariate_elliptic_jackson(a, b, c, d, x, q, N, n) CONSTRUCTS the right-hand side as a single canonical EllRatio (the unit prefactor, the vector-Pochhammer numerator thetas of each num base aq, aq/bc, aq/bd, aq/cd, the vector-Pochhammer denominator thetas of each den base aq/b, aq/c, aq/d, aq/bcd); the EllRatio constructor folds each theta's canonicalize prefactor, cancels matching thetas, and sorts the survivors. The remaining parameter e is fixed by the balancing e = a²q^{N+1}/(bcd·x^{n-1}) (so the sum is balanced by construction). The n=1 case is the Frenkel–Turaev ₈ω₇ sum; the induction on N runs through Warnaar's Lemma 2.2 + the elliptic partial-fraction expansion (rc95).
MPM-VERIFIED at build. The closed form equals the actual n-fold Cₙ very-well-poised sum over partitions at n=2 (N=1,2) + n=3 (N=1) via an INDEPENDENT exact-ℚ theta oracle, plus Warnaar's Lemma 2.2 pinned in isolation (test_multivariate_elliptic_jackson_rc96.py). The 1:1 native C peer srmech_multivariate_elliptic_jackson builds the SAME EllRatio over the shared srmech_ellbase_* monomial algebra + er_build (mirrors rc94's single-EllRatio path); the native EllRatio is trusted ONLY after it is rebuilt and confirmed == the pure-Python EllRatio (the COMPLETE alternative + the parity oracle). Exact over the modified-theta algebra (no float), no abs() (Class-K sign), no numpy / math; C peer caller-arena / malloc-free / JPL-clean.
[0.9.0rc95] - 2026-07-01¶
elliptic_partial_fraction — the ELLIPTIC PARTIAL-FRACTION expansion, the reduction ENGINE of the multivariable root-system Cₙ elliptic reduction row. rc94 shipped the elliptic Cauchy / Frobenius DETERMINANT (the FOUNDATION of the Cₙ row); this rc ships the ENGINE it (and Warnaar's Lemma 2.2, and the Cₙ elliptic Jackson summation) is proved with. Where the single-variable ₈ω₇ reduces to the Weierstrass THREE-TERM relation (ThetaSum.three_term), the multivariable Cₙ objects all reduce to THIS partial-fraction expansion. The op IS a new ToolEntry → tools.total 345 → 346. ABI stays 3 (additive C symbol). Shipped CO-EQUAL Python + 1:1 native C peer in the SAME rc (the everything-mirrors / never-split discipline).
THE EXPANSION. For the variable x and distinct z₁…zₙ, y₁…yₙ (Hjalmar Rosengren, Elliptic Hypergeometric Functions, arXiv:1608.06161v3 [math.CA] (2017), Proposition 1.6.1 + Eq. (1.22)):
∏_{k=1}^n θ(x/z_k; p)/θ(x/y_k; p)
= 1/θ(Y/Z; p) · Σ_{j=1}^n
[ ∏_k θ(y_j/z_k; p) / ∏_{k≠j} θ(y_j/y_k; p) ]
· [ θ(x·Y/(y_j·Z); p) / θ(x/y_j; p) ], Y = ∏y, Z = ∏z.
elliptic_partial_fraction(x, zs, ys) CONSTRUCTS the right-hand side as an exact ThetaSum — a SUM of n theta-quotient terms (each summand j an EllRatio with the unit prefactor, n+1 numerator thetas + n+1 denominator thetas incl. the load-bearing 1/θ(Y/Z) factor — the 1/θ(t) of the Prop. 1.6.1 interpolation with t = Y/Z). This is the FIRST ThetaSum-returning C dispatch.
MPM-VERIFIED at build. The constructed sum equals the left-hand product ∏_k θ(x/z_k)/θ(x/y_k) at n=1..4 — the carrier's own exact-ℚ truncated-theta eval (test_elliptic_partial_fraction_rc95.py). The 1:1 native C peer srmech_elliptic_partial_fraction builds the SAME n EllRatio terms over the shared srmech_ellbase_* monomial algebra + er_build (there is no ThetaSum-CONSTRUCTION C surface, so — exactly like srmech_elliptic_lagrange_basis returns its k basis EllRatios — the peer returns the n term EllRatios and the Python side sums them via ThetaSum.from_ellratio + +, identically to the pure path). The native ThetaSum is trusted ONLY after it is rebuilt and confirmed == the pure-Python ThetaSum (the COMPLETE alternative + the parity oracle). Exact over the modified-theta algebra (no float), no abs() (Class-K sign), no numpy / math; C peer caller-arena / malloc-free / JPL-clean.
[0.9.0rc94] - 2026-07-01¶
elliptic_cauchy_determinant — the ELLIPTIC-DETERMINANT primitive (Frobenius's elliptic Cauchy determinant evaluation), the FOUNDATION of the multivariable root-system Cₙ elliptic reduction row. The single-variable elliptic Σ-row is complete (elliptic_gosper rc65 → elliptic_recurrence_8w7 rc68 → elliptic_zeilberger rc90 → elliptic_wz_certificate rc91, all auto-routed since rc92). The multivariable Cₙ row needs a genuinely larger primitive: where the single-variable ₈ω₇ reduces to the Weierstrass THREE-TERM relation (ThetaSum.three_term), the Cₙ objects reduce to the elliptic PARTIAL-FRACTION expansion + the elliptic Cauchy / Frobenius DETERMINANT. This rc ships that determinant as an exact constructive op. The op IS a new ToolEntry → tools.total 344 → 345. ABI stays 3 (additive C symbol). Shipped CO-EQUAL Python + 1:1 native C peer in the SAME rc (the everything-mirrors / never-split discipline).
THE CLOSED FORM. For distinct x₁…xₙ, y₁…yₙ and a parameter t (Hjalmar Rosengren, Elliptic Hypergeometric Functions, arXiv:1608.06161v3 [math.CA] (2017), Exercise 1.6.6 — Frobenius's determinant evaluation, classically Frobenius 1882, proved via the elliptic partial-fraction expansion Eq. (1.22)):
det_{1≤i,j≤n} [ θ(t·x_i·y_j; p) / θ(x_i·y_j; p) ]
= θ(t; p)^{n-1} · θ(t·x₁…xₙ·y₁…yₙ; p)
· ∏_{i<j} [ x_j·y_j · θ(x_i/x_j; p)·θ(y_i/y_j; p) ] / ∏_{i,j} θ(x_i·y_j; p).
elliptic_cauchy_determinant(t, xs, ys) CONSTRUCTS the right-hand side as a single canonical EllRatio (the exact EllMonomial prefactor ∏_{i<j} x_j·y_j, the numerator thetas θ(t)×(n-1), θ(t·∏x·∏y), θ(x_i/x_j) + θ(y_i/y_j) for i<j, and the denominator thetas θ(x_i·y_j) for all i,j); the EllRatio constructor folds each theta's canonicalize prefactor, cancels matching thetas, and sorts the survivors. A CONSTRUCTIVE elliptic identity op (the peer of ThetaSum.three_term).
MPM-VERIFIED at build. The constructed closed form equals the theta-matrix determinant at n=1..4 — the carrier's own exact-ℚ truncated-theta eval of the closed form vs a Leibniz determinant of the same θ(t·x_i·y_j)/θ(x_i·y_j) matrix (test_elliptic_determinant_rc94.py). The 1:1 native C peer srmech_elliptic_cauchy_determinant constructs the SAME EllRatio over the shared srmech_ellbase_* monomial algebra + er_build (byte-exact to the Python carrier; the pure-Python body is the complete alternative + the parity oracle; the native path is confirmed equal to the pure construction at build). Exact over the modified-theta algebra (no float), no abs() (Class-K sign), no numpy / math; C peer caller-arena / malloc-free / JPL-clean.
[0.9.0rc93] - 2026-07-01¶
Fix: ThetaSum.is_zero is now a TOTAL function — a native size-guard trip never crashes the decision. Found while probing the multivariate Cₙ elliptic reduction row (Rosengren's Lemma 2.2): the srmech_thetasum_is_zero C peer returned non-OK SRMECH_ERR_OVERFLOW (status 4) on a large / multivariate cleared certificate, and _is_zero_c propagated it as a RuntimeError — crashing an otherwise-decidable is_zero. Root cause: the caller-arena's bigint limb bound was sized to the INPUT coefficients, but the Weierstrass three-term reduction (the rewrite + canonical-pair inversion prefactors) MULTIPLIES them, so the working bigints outgrow the input.
Two-part fix (Python-side; no C source change, ABI stays 3): (1) _is_zero_c catches the native failure and falls back to _is_zero_py — the C peer is an optimization, never the sole authority, and the pure-Python path is the complete parity oracle; (2) the native coefficient-limb provisioning gets intermediate-growth headroom (scaled by the theta count) so the native fast path handles large / multivariate certificates directly instead of falling back. Regression-tested (test_thetasum_overflow_fallback_rc93.py): the Lemma 2.2 (n=2) cleared certificate now decides without crashing and its native verdict equals the pure-Python verdict; known-zero (three-term) and known-nonzero identities still decide correctly through the native path. numpy-free; no abs(). Unblocks the multivariate elliptic-row R&D (the exact certificates there are large).
[0.9.0rc92] - 2026-07-01¶
dispatch.infer auto-routes the ELLIPTIC Σ sub-row (sigma_elliptic) — the F929 dispatch table now reaches the Frenkel–Turaev ₈ω₇ / ₁₀E₉ reducers. The elliptic Σ-row completed at rc91 (elliptic_gosper rc65 → elliptic_recurrence_8w7 rc68 → elliptic_zeilberger rc90 → elliptic_wz_certificate rc91); this rc wires it into the ONE F929 infer meta-dispatcher (the same way rc58 wired the sigma_multivar / sigma_q sub-rows). An arbitrary stored relationship whose structure matches the ₈ω₇ term-ratio — an EllRatio under elliptic_term_ratio, or an explicit row/kind tag (sigma_elliptic / elliptic / 8w7 / 10e9 / frenkel_turaev) — now auto-DETECTS as sigma_elliptic, routes to elliptic_wz_certificate (the identity PROOF), is accepted ONLY when its own verified flag is True (the anti-hallucination gate), and returns the closed form cf(n) — else an honest OPEN with the truthful next-theory hint (a multivariate Aₙ/Cₙ elliptic multisum or a higher-genus theta reduction row). The sigma_q OPEN hint is updated (the elliptic row it named as next-theory now ships).
Non-compute orchestration — no new op, no C peer, tools.total unchanged (344), ABI stays 3. infer composes the already-C-mirrored reducers and runs no arithmetic of its own (the from_bodies / cooccurrence_edges non_compute precedent, same as the rc51/rc58 router rows). numpy-free; no abs(). This makes all four elliptic ops reachable through the single F929 dispatch table, completing the elliptic row's integration (cyclic → the_one, spectral → resonant_spectrum, Σ → telescope/gosper/zeilberger/wz + the multivar / q / elliptic sub-rows).
[0.9.0rc91] - 2026-06-30¶
elliptic_wz_certificate — the ELLIPTIC Σ-row IDENTITY-PROOF op for the Frenkel–Turaev ₈ω₇ SUMMATION (the CAPSTONE elliptic Σ-row rung; proves Σ_k F(n,k) = cf(n) EXACTLY and returns the closed form cf(n)). The elliptic Σ-row had elliptic_gosper (rc65, indefinite), elliptic_recurrence_8w7 (rc68, the order-1 finder) and elliptic_zeilberger (rc90, the recurrence + EXACT certificate). rc91 ships the genuine elliptic analogue of wz_certificate (the §76 ordinary/q identity-proof rung): where elliptic_zeilberger proves the RECURRENCE f(n+1) = ρ(n)·f(n), this op proves the full SUMMATION IDENTITY and its DISTINCT OUTPUT is the closed form cf(n) = (aq, aq/bc, aq/bd, aq/cd; q,p)_n / (aq/b, aq/c, aq/d, aq/bcd; q,p)_n (Warnaar Cor 2.2 / Rosengren Thm 2.3.1). The op IS a new ToolEntry → tools.total 343 → 344. ABI stays 3 (additive C symbol). Shipped CO-EQUAL Python + 1:1 native C peer in the SAME rc (the everything-mirrors / never-split discipline).
THE EXACT PROOF (connection-coefficient INDUCTION, no 1e-9). A literal Wilf–Zeilberger pair — a G(n,k) = R(n,k)·F(n,k) antidifference — is provably dead for the elliptic case (the rc90 finding: the Gosper–Petkovšek certificate lives on the squared lattice with an irreducible quadratic core). So the identity P(n): Σ_{k=0}^n F(n,k) = cf(n) is proved by the literature's connection-coefficient induction (Rosengren §2.3): the BASE CASE P(0) (the ₈ω₇ summand's terminating (q^{-n}; q,p)_k factor leaves only k=0, so Σ_{k=0}^0 F(0,k) = F(0,0) = 1 = cf(0)) plus the INDUCTIVE STEP P(n) ⟹ P(n+1) (Σ F(n+1,k) / Σ F(n,k) = ρ(n) = cf(n+1)/cf(n), whose EXACT certificate is the SAME cleared connection-coefficient ±-pair split elliptic_zeilberger builds — _connection_split_certificate — decided ≡ 0 in the exact additive ThetaSum carrier via ThetaSum.is_zero, never a converging witness). Together P(0) ∧ (P(n) ⟹ P(n+1)) is the complete exact proof of the Frenkel–Turaev summation.
THE API. elliptic_wz_certificate(r) — r the ₈ω₇ summand's term ratio t(n+1)/t(n) = r(x) (x = qⁿ, an EllRatio). Returns {'identity': '…', 'closed_form': {'num': [4 bases], 'den': [4 bases]}, 'certificate': {'method': 'connection_coefficient_induction', 'exact': True, …}, 'verified': True} ONLY when r is a canonical ₈ω₇ AND the certificate decides ≡ 0; else None (the honest out-of-class residue). No float on the decision path, no abs() (Class-K sign), no numpy / math.
MPM source (verified at build by reading the source PDF): Hjalmar Rosengren, Elliptic Hypergeometric Functions, arXiv:1608.06161v3 [math.CA] (2017), Theorem 2.3.1 (the Frenkel–Turaev ₈ω₇ summation); §2.3 Eqs. (2.12)–(2.15) reduce to §1.4 Eq. (1.12), the Weierstrass three-term relation; the closed product form is Warnaar, Constr. Approx. 18 (2002) 479–502, Corollary 2.2.
The C peer (everything-mirrors, same-rc). srmech_elliptic_wz_certificate is a 1:1 mirror of srmech_elliptic_zeilberger: it runs the SAME recognize-decompose pipeline + builds the connection-coefficient split certificate and decides it ≡ 0 via the shared srmech_thetasum_is_zero kernel. The summation proof reduces to the SAME certificate decision, so the peer returns only the verdict (has = 1 iff recognized AND the certificate is exactly zero); the Python builds the closed-form Pochhammer endpoints on its side (the analogue of “the Python builds ρ”) and trusts a native has=1 ONLY after the pure path agrees AND the certificate re-decides ≡ 0 in exact ℚ. Caller-arena (malloc-free, JPL Power-of-Ten clean: no goto/recursion; ≤60-line functions; ≥2 asserts/function; the ±1 prefactor sign + the magnitude-½ x-power test are Class-K pin-slot branches, never abs()). Built + verified under Release -DNDEBUG + -Werror pedantic; the full suite runs numpy-ABSENT.
[0.9.0rc90] - 2026-06-30¶
elliptic_zeilberger — the EXACT elliptic Σ-row CREATIVE-TELESCOPING op for the Frenkel–Turaev ₈ω₇ summation (the THIRD elliptic Σ-row rung; rc68's 1e-9 numerical gate REPLACED by an EXACT proof). The elliptic Σ-row already had elliptic_gosper (rc65, indefinite) and elliptic_recurrence_8w7 (rc68, the order-1 recurrence FINDER — whose verification gate was a 1e-9 numerical convergence check). rc90 ships the genuine elliptic analogue of zeilberger / q_zeilberger: the order-1 recurrence f(n+1) = ρ(n)·f(n) PLUS an EXACT connection-coefficient certificate that PROVES it. The op IS a new ToolEntry (the elliptic peer of the §76 Σ-row reducers) → tools.total 342 → 343. ABI stays 3 (additive C symbol). Shipped CO-EQUAL Python + 1:1 native C peer in the SAME rc (the everything-mirrors / never-split discipline).
THE EXACT PROOF (no 1e-9, no convergence witness). Where elliptic_recurrence_8w7 only checked the constructed ρ(n) numerically against the closed product form, this op CERTIFIES the recurrence by deciding the connection-coefficient inductive-step identity ≡ 0 in the exact additive ThetaSum carrier (rc62 is_zero: quasi-periodicity grouping + the exact Weierstrass three-term reduction — never a converging eval_trunc). The certificate is the cleared ±-pair split (Rosengren, see below), with N = qⁿ, K = qᵏ, the theta variable x, and θ(u·v^±) = θ(uv)·θ(u/v):
θ(bcN,(b/c)K²/N)·θ(aN·x^±) − θ(acN²/K,(a/c)K)·θ(bK·x^±) + (b/c)(K²/N)·θ(abNK,(a/b)N/K)·θ(cN/K·x^±) ≡ 0
Every term factors into two clean ±-pairs with perfect-square monomial midpoints (aN, bK, cN/K — no irrational √), exactly the carrier-reducible shape ThetaSum.is_zero decides (the collapsed single-theta form of the Weierstrass relation hides an irrational √(b/c) midpoint and is NOT carrier-reducible; the cleared ±-pair form is). The certificate is built from the RAW split (NOT via ThetaSum.three_term), so is_zero == True is a genuine exact verification of THIS ₈ω₇ instance.
WHY the connection-coefficient split (not a Gosper/WZ antidifference). The elliptic ₈ω₇ does NOT admit a Gosper–Petkovšek R(n,k) creative-telescoping certificate of the ordinary/q kind (the term-ratio lives on the squared lattice with an irreducible quadratic core; the search is provably dead). The literature proof (Rosengren §2.3) is the connection-coefficient expansion h_n(x;a) = Σ_k C^n_k h_k(x;b) h_{n-k}(x;c) whose elliptic binomial coefficient C^n_k satisfies the elliptic Pascal recurrence — which reduces, after cancellation, to a single instance of the Weierstrass three-term theta relation. So the inductive step IS the three-term reduction; verifying it ≡ 0 (with the trivial base case C^0_0 = 1) is the complete exact proof.
THE API. elliptic_zeilberger(r) — r the ₈ω₇ summand's term ratio t(n+1)/t(n) = r(x) (x = qⁿ, an EllRatio). Returns {'order': 1, 'coeffs': [-ρ, 1], 'rho': ρ, 'certificate': {'kind': 'connection_coefficient_split', 'exact': True, …}, 'verified': True, …} (f(n+1) = ρ(n)·f(n) with the EXACT certificate) ONLY when r is a canonical ₈ω₇ AND the certificate decides ≡ 0; else None (the honest out-of-class / certificate-did-not-close residue). The ρ is byte-identical to the rc68 elliptic_recurrence_8w7 ρ (the SAME recognize-decompose-construct; only the GATE differs — numerical → exact). No float on the decision path, no abs() (Class-K sign), no numpy / math.
MPM source (verified at build by reading the source PDF): Hjalmar Rosengren, Elliptic Hypergeometric Functions, arXiv:1608.06161v3 [math.CA] (2017), §2.3 Eqs. (2.12)–(2.14) [the connection-coefficient expansion + the elliptic binomial coefficient + the elliptic Pascal recurrence] → §1.4 Eq. (1.12) [the Weierstrass three-term relation]; the closed product form + ρ are Warnaar, Constr. Approx. 18 (2002) 479–502, Corollary 2.2.
The C peer (everything-mirrors, same-rc). srmech_elliptic_zeilberger orchestrates the existing kernels: it runs the SAME recognize-decompose pipeline as srmech_elliptic_recurrence_8w7 (the shared srmech_ellbase_* monomial algebra + er_build), then builds the connection-coefficient split certificate's ThetaSum terms (each theta theta-canonicalized + its quasi-periodicity prefactor folded into the term coefficient, exactly as the Python ThetaSum constructor) and decides them ≡ 0 via the shared srmech_thetasum_is_zero kernel. It returns only the verdict (has = 1 iff recognized AND the certificate is exactly zero); the Python builds ρ on its side and trusts a native has=1 ONLY after the pure path agrees AND the certificate re-decides ≡ 0 in exact ℚ. Caller-arena (malloc-free, JPL Power-of-Ten clean: no goto/recursion; ≤60-line functions; ≥2 asserts/function; the ±1 prefactor sign + the magnitude-½ x-power test are Class-K pin-slot branches, never abs()). Built + verified under Release -DNDEBUG + -Werror pedantic (the CI-matching config); the full suite runs numpy-ABSENT.
[0.9.0rc89] - 2026-06-30¶
QuasiModularFormsRing — the level-1 ℂ[E₂,E₄,E₆] QUASIMODULAR-forms ring + its EXACT membership decision (the FOURTH WEIGHT-axis rung; the rc84 ModularFormsRing pattern ONE generator up). rc83's eisenstein.py REJECTS k = 2 and NAMES this theory in its own docstring: E₂ is NOT a modular form (M₂(SL₂(ℤ)) = {0}; it picks up the (12/2πi)(c/(cτ+d)) anomaly under τ → −1/τ). rc89 builds the smallest ring that contains it — the ring of QUASIMODULAR forms M̃_*(SL₂(ℤ)) = ℂ[E₂,E₄,E₆] (Kaneko–Zagier). The REDUCER quasimodular_represent IS a new ToolEntry (the WEIGHT-axis analog of the §76 Σ-row gosper/zeilberger/wz_certificate reducers; named quasimodular_represent — not quasimodular_forms_ring_represent — so the mapped Anthropic tool name stays within the 64-char grammar ceiling) → tools.total 341 → 342; eisenstein_e2 + the bare ring constructor + weight_monomials/dim are NOT ToolEntries. ABI stays 3 (additive C symbols). Shipped CO-EQUAL Python + 1:1 native C peer in the SAME rc (the everything-mirrors / never-split discipline).
THE OBJECT. Every quasimodular form of weight k is a UNIQUE exact-ℚ polynomial in E₂, E₄, E₆ — a finite ℚ-combination of the weight-k monomials E₂^a · E₄^b · E₆^c with 2a + 4b + 6c = k, a,b,c ≥ 0. The rc84 modular ring ℂ[E₄,E₆] is exactly the a = 0 subring, so this ring genuinely EXTENDS the modular one. eisenstein_e2(n_terms) is the weight-2 generator E₂ = 1 − 24·Σσ₁(n)qⁿ = [1, −24, −72, −96, −168, …] (the prefactor is the SAME normalized-Eisenstein −2k/B_k at k = 2: −4/B₂ = −4/(1/6) = −24, computed by the SAME Bernoulli cascade as the modular E_k — NOT a magic literal). The rc83 Eisenstein(k) carrier KEEPS its k ≥ 4 modular contract and still rejects k = 2; E₂ enters ONLY through this quasimodular path (the honest separation of objects).
THE KEYSTONES — Ramanujan's Serre-derivative identities (the defining quasimodular structure). The quasimodular ring is exactly the world the Serre/Ramanujan derivative D = q·d/dq (the op D(Σ a_n qⁿ) = Σ n·a_n qⁿ) closes on. On the generators it is Ramanujan's system of three differential equations (Ramanujan, On certain arithmetical functions, Trans. Camb. Phil. Soc. 22 (1916), pp. 159–184 — his P = E₂, Q = E₄, R = E₆): D E₂ = (E₂²−E₄)/12, D E₄ = (E₂E₄−E₆)/3, D E₆ = (E₂E₆−E₄²)/2. These were DERIVED + checked bit-exactly on the q-series FIRST (no recall-and-trust), THEN confirmed through represent: represent(D E₄, 6) → {(1,1,0): 1/3, (0,0,1): −1/3}; represent(D E₂, 4) → {(2,0,0): 1/12, (0,1,0): −1/12}; represent(D E₆, 8) → {(1,0,1): 1/2, (0,2,0): −1/2}.
THE EXTENDS-THE-MODULAR-RING PROOF. E₂² (weight 4) is a quasimodular monomial that is NOT in ℂ[E₄,E₆]: rc89 quasimodular_represent(E₂², 4) → {(2,0,0): 1}, but rc84 modular_forms_ring_represent(E₂², 4) → None. The membership decision is decompose-and-compute (build the E₂/E₄/E₆ monomial columns by exact-ℚ truncated q-series multiply, solve the square subsystem with the rc40 QMat exact-ℚ Gauss-Jordan, then VERIFY every provided term), NOT a search; None is the honest rejection.
THE HONEST OPENs (named, not faked). (1) Jacobi forms — the next WEIGHT rung, the τ–z two-variable bridge to the elliptic carriers (Eichler–Zagier, The Theory of Jacobi Forms, Progr. Math. 55, 1985); it needs a τ–z 2-variable carrier (a bigger build), NOT built here. (2) Level N > 1 — this is the level-1 quasimodular ring; M̃_*(Γ₀(N)) needs more generators, NOT built here (the level axis, one generator up from the rc84 boundary).
MPM sources (verified): Ramanujan, On certain arithmetical functions, Trans. Camb. Phil. Soc. 22 (1916), pp. 159–184 (the P,Q,R differential equations); Kaneko & Zagier, A generalized Jacobi theta function and quasimodular forms, in The Moduli Space of Curves, Progr. Math. 129, Birkhäuser (1995), pp. 165–172; Zagier, Elliptic Modular Forms and Their Applications, in The 1-2-3 of Modular Forms, Springer (2008), §5.3 "Quasimodular forms".
The C peer (everything-mirrors, same-rc). srmech_quasimodular_forms_ring_represent mirrors the membership solve: it builds the weight-k monomial-basis matrix from the E₂/E₄/E₆ q-series (the rc83 srmech_eisenstein_qseries — at k = 2 for E₂ via its new quasimodular branch, k = 4/6 for E₄/E₆ — + an exact-ℚ truncated convolution), DISPATCHES the square subsystem to the existing srmech_qmat_solve (reuse, not reimplement), VERIFIES all terms, and returns the reduced (num,den) rep or a no-solution flag. The rc83 srmech_eisenstein_qseries gate is relaxed from k ≥ 4 to k ≥ 2 (the q-series formula is identical at k = 2; the modular/quasimodular DECISION stays Python-side). Caller-arena (malloc-free, JPL Power-of-Ten clean: no goto/recursion; ≤60-line functions; ≥2 asserts/function; OVERFLOW-not-wrap; the sign is the Class-K pin-slot branch, never abs()). Python == C byte-exact parity — compared element-for-element over the keystones (DE₂/DE₄/DE₆, E₂², E₂, E₄, E₆, a non-form) + an E_k-sweep (k = 4…14); the C is NOT trusted, it is compared. Built + parity-verified under Release -DNDEBUG + -Werror pedantic (the CI-matching config). No numpy/float/math; exact ℚ only.
[0.9.0rc88] - 2026-06-29¶
RiemannTheta.addition_holds_at / RiemannThetaG3.addition_holds_at — the GENUINE Fay/Hirota bilinear VERIFIER at GENERIC RATIONAL arguments (the KP-shadow op on the rc87 theta_at foundation). rc87 shipped theta_at (exact theta at a rational argument = an exact cyclotomic ℤ[ζ_m] lattice, m = 2·z_den). rc88 USES it to verify, EXACTLY, the genus-g Riemann theta addition formula in terms of SECOND-ORDER theta functions at generic rational arguments — the regime the existing half-characteristic addition_holds/goepel_holds (integer theta-NULL gates at z = 0) cannot reach. A CARRIER verifier method (the addition_holds/goepel_holds precedent): NO new ToolEntry → tools.total stays 341. ABI stays 3 (additive C symbol). Shipped CO-EQUAL Python + 1:1 native C peer in the SAME rc (the everything-mirrors / never-split discipline).
THE EXACT FORMULA (verified, no float). For the carrier's LOWER characteristic ε (upper char ε' = 0):
θ[0;ε](x+y|Ω) · θ[0;ε](x−y|Ω) = Σ_{α∈{0,1}^g} (−1)^{ε·α} · θ[α;0](2x|2Ω) · θ[α;0](2y|2Ω)
— Riemann's theta addition formula, the genus-g form θ(z+w)·θ(z−w) = Σ_{ξ∈ℤ^g/2ℤ^g} Θ_ξ(z)·Θ_ξ(w) over the second-order theta functions Θ_ξ(z) = θ[ξ/2;0](2z|2Ω). MPM source: Igusa, Theta Functions (Grundlehren 194, Springer 1972), Ch. IV (theta constants & second-order theta functions); Mumford, Tata Lectures on Theta I (Birkhäuser 1983), Ch. II (the addition formula). The ε = 0 case is the classical second-order addition formula; the (−1)^{ε·α} sign is the lower-char ε ≠ 0 case (θ[0;ε] = θ[0;2ε]-translate; θ[α;2ε] = (−1)^{ε·α}θ[α;0]). The exact form was VERIFIED bit-exactly against the rc87 theta_at on this carrier before shipping (derived from the lattice definition + checked dict-to-dict over z_den ∈ {2,3,4,5,6,7,8}, ε ∈ {0,(1,0),(1,1),…}, box-stable).
THE API. addition_holds_at(x_num, y_num, z_den, box) -> bool (instance method; x = x_num/z_den, y = y_num/z_den; x_num/y_num integer g-tuples). addition_at_lhs / addition_at_rhs expose the two exact cyclotomic lattices. Returns True iff the two sides agree exactly on the SAFE INNER REGION the box provably resolves (diagonal exponents Aᵢ ≤ 4·box²), the region is non-trivially populated, and it genuinely exercises a genus-g cross-term (C ≠ 0). No float, no numpy, no abs() (the (−1)^{ε·α} sign is the Class-K pin-slot; the cyclotomic phase is the Class-I cyclic exponent).
THE NO-SHELL GATES (all exact, all PASS — proving it is NOT a Göpel-duplicate). (1) half-period bridge: at half-period arguments (z_den = 2) addition_holds_at returns True — the regime the half-character addition_holds covers (which also passes); the generic op SUBSUMES the half-period special case (algebraically, at half-period args the formula reduces to a theta-CONSTANT identity among upper characteristics, the addition_holds world). (2) GENERIC-argument content (the real gate): at GENERIC rational x,y with z_den = 7 (m = 14, φ(14) = 6) addition_holds_at returns True with genuinely NON-REAL cyclotomic coefficients (nonzero power-basis coordinates beyond index 0, e.g. (0,0,0,−2,2,0) ∈ ℤ[ζ₁₄]) — the regime the integer theta-NULL gates CANNOT reach. (NOTE: z_den = 6/5/10 collapse to rational coefficients by Niven's theorem — z_den = 7 is the honest non-rational-cyclotomic gate.) (3) honest is-Jacobian OPEN scope (documented, NOT a built op): RiemannTheta.kp_bilinear_scope_note() states that the op verifies the ABSTRACT theta-bilinear (Fay/Hirota-family) identity — a genus-g shadow of the KP Hirota bilinear hierarchy — but does NOT decide is-Jacobian / the curve-specific Fay trisecant (that needs the prime form + curve points + the KP τ-solution = the Schottky problem, the operand-side OPEN, genuinely open for genus ≥ 5). The abstract bilinear identity is REPRESENTABLE; the is-Jacobian decision is the named operand-side OPEN — mirrors the schottky_*_is_open honesty pattern. A nonzero UPPER characteristic ε' (which lands the two-argument addition at Ω/2, outside the carrier's Ω/2Ω lattices) is REJECTED — an honest boundary, not a fabricated reduction.
The C peer (everything-mirrors, same-rc). srmech_riemann_theta_cyc_mul — the EXACT ℤ[ζ_m] power-basis MULTIPLY, the genuinely-new exact-integer kernel: out[deg] ← a[deg]·b[deg], (Σᵢ aᵢζ^i)(Σⱼ bⱼζ^j) = Σ aᵢbⱼ ζ^{i+j} with each ζ^{i+j} reduced via the REUSED rc29 exact-DFT reduction table (table[(i+j) mod m]). The verifier's theta-at-argument term emission is ALREADY C-backed (rc87 srmech_riemann_theta_at / _g3_at); the cyclotomic accumulation is Python (rc87 precedent); the bilinear PRODUCT multiplies the cyclotomic coefficients (this kernel) while convolving the integer exponent keys (Python "caller bookkeeping" — the rc73 addition_holds / rc74 Göpel-gate precedent: the gate convolution rides the C-backed kernels). Caller-arena (malloc-free, JPL Power-of-Ten clean: no goto/recursion; ≤60-line functions; ≥2 asserts/function; the int64 fast path GUARDS per-coefficient magnitude — a Class-K sign-branch range read, never abs() — returning SRMECH_ERR_OVERFLOW so the caller runs the pure-Python bignum body, the COMPLETE alternative). Python == C byte-exact parity — the C kernel compared element-for-element against the pure _cyc_mul_py over a sweep of vectors/rings, AND the full addition_holds_at gate run end-to-end with the native path live. The C is NOT trusted — it is compared. Built + parity-verified under Release -DNDEBUG + -Werror pedantic (the CI-matching config).
Genus scope. Genuine at g2 RiemannTheta + g3 RiemannThetaG3 (the formula ports identically one genus up — Σ_{α∈{0,1}³}, 8 terms). g4/g5 get addition_holds_at in a follow-up (the (2·box+1)^g box-blowup is the only reason; the verifier itself is genus-uniform).
[0.9.0rc87] - 2026-06-29¶
RiemannTheta.theta_at / RiemannThetaG3.theta_at — EXACT theta evaluation at a RATIONAL argument (the genus-axis Fay-trisecant / KP-Hirota verifier FOUNDATION). The genus carriers (RiemannTheta g2 → RiemannThetaG5) are theta-CONSTANT carriers: θ[ε';ε](0|Ω) at argument z = 0, with binary characteristics, held as exact-INTEGER q-lattices. Their addition/duplication/Göpel identities are the half-characteristic shadows of the Fay/KP bilinear structure. The genuine Fay-trisecant verifier (the NEXT rung, rc88) needs theta at a GENERIC argument — which the integer carriers cannot represent. rc87 builds that missing foundation: exact theta at a RATIONAL argument z = z_num/z_den (z_den even = 2N), which IS exactly representable because a rational argument turns each lattice term's extra Fourier factor into a ROOT OF UNITY (an exact cyclotomic integer). A CARRIER eval method (like lattice()): NO new ToolEntry → tools.total stays 341. ABI stays 3 (additive C symbols). Shipped CO-EQUAL Python + 1:1 native C peers in the SAME rc (the everything-mirrors / never-split discipline).
THE MATH (exact, no float). θ[ε';ε](z|Ω) = Σ_{n∈ℤ^g} exp(πi(n+ε'/2)·Ω·(n+ε'/2) + 2πi(n+ε'/2)·(z+ε/2)). The carrier already encodes the Ω-part as the integer quarter-nome lattice + the (−1)^{ε·n} sign. For a RATIONAL argument z = z_num/z_den the EXTRA factor exp(2πi(n+ε'/2)·z) = ζ_m^{Σᵢ (2nᵢ+ε'ᵢ)·z_numᵢ} where ζ_m = exp(2πi/m), m = 2·z_den is a primitive m-th root of unity — so each q-monomial coefficient becomes an EXACT element of the cyclotomic ring ℤ[ζ_m] (an integer vector in the power basis {1, ζ, …, ζ^{φ(m)-1}}), times the existing Class-K sign. No transcendental evaluation — exact cyclotomic arithmetic. The cyclotomic ring is REUSED (not reinvented) from the rc29 exact-DFT engine (srmech.amsc.cascade.exact_dft._cyclotomic_reduction) — the same ℤ[ζ_N] power-basis representation the exact DFT runs on.
THE API. theta_at(z_num, z_den, box) — z_num an integer tuple of length g (2 for RiemannTheta, 3 for RiemannThetaG3) over an even denominator z_den = 2N; returns the EXACT cyclotomic {exponent-key: ℤ[ζ_m] coeff vector} lattice truncated to |nᵢ| ≤ box. Keys are the SAME quarter-nome integer exponents as lattice(); each coeff is an integer vector of length φ(m). No float, no numpy, no abs() (the (−1)^{ε·n} sign is the Class-K pin-slot; the root-of-unity phase is the exact Class-I cyclic exponent).
THE NO-SHELL GATES (all exact, all PASS — proving it GENUINELY generalizes, isn't a Göpel-duplicate). (1) z = 0 bridge: theta_at((0,…), z_den, box) reduces BIT-EXACTLY to the existing integer lattice(box) (every cyclotomic coeff collapses to its integer value [c,0,…,0]) — proves the foundation EXTENDS the representable core, doesn't replace it. (2) half-period bridge: at z = a half-period (z_num a binary vector, z_den = 2, m = 4) theta_at reproduces the existing characteristic-shifted theta θ[ε'; ε⊕δ] up to the EXACT global root-of-unity automorphy factor ζ_4^{ε'·δ} (verified over ALL 64 genus-2 characteristic/half-period cases) — the standard half-period shift θ[ε';ε](δ/2) ∝ θ[ε';ε⊕δ]. (3) quasi-periodicity: θ(z + λ) for an integer lattice vector λ multiplies every coeff by the exact sign (−1)^{ε'·λ} (verified for several λ over a GENERIC rational z, z_den = 6, m = 12, φ(m) = 4 — where the coeffs are genuinely non-real cyclotomic integers, i.e. theta_at spans BEYOND the half-character carriers).
The C peers (everything-mirrors, same-rc). srmech_riemann_theta_at (g2; + _count) emits one [A,B,C,e_mod,sign] quintuple per lattice point; srmech_riemann_theta_g3_at (g3; + _count) emits the [A1,A2,A3,C12,C13,C23,e_mod,sign] octuple. Each mirrors the genuinely-new exact-integer per-term content — the SAME quarter-nome exponents as the lattice peer PLUS the phase exponent e_mod = (Σᵢ uᵢ·z_numᵢ) mod m (the root-of-unity exponent, a Class-I cyclic reduction into [0,m)) PLUS the Class-K sign — over a caller arena (malloc-free, JPL Power-of-Ten clean: no goto/recursion; ≤60-line functions; ≥2 asserts/function; OVERFLOW-not-wrap; the sign is a branch, never abs()). The Python theta_at parses the flat C array into the SAME (key, e_mod, sign) term stream the pure path produces, then accumulates sign·ζ_m^{e_mod} via the ONE shared cyclotomic accumulator — so the native and pure paths are byte-identical by construction. The C is NOT trusted — it is compared (Python == C byte-exact parity across a sweep of characteristics, z-arguments, and boxes, g2 box ≤ 3 / g3 box ≤ 2). Built + parity-verified under Release -DNDEBUG + -Werror pedantic (the CI-matching config).
Genus scope. The foundation is genuine at g2 + g3 (the genus where the rc88 trisecant lands). g4/g5 (RiemannThetaG4/RiemannThetaG5) get theta_at in a follow-up — the pattern ports identically (uᵢ = 2nᵢ+ε'ᵢ, Aᵢ = uᵢ², C_ij = uᵢuⱼ, e = Σ uᵢ z_numᵢ), deferred only to keep the box-blowup (2·box+1)^g tests bounded; it is the documented next step, not a shell (the FOUNDATION is genuine at g2/g3).
[0.9.0rc86] - 2026-06-29¶
srmech.amsc.riemann_theta.RiemannThetaG5 — the GENUS-5 Riemann theta-CONSTANT carrier + the honest GENUINELY-OPEN genus-5 Schottky decision (the genus axis pushed PAST the Schottky frontier). The next rung of the genus-axis operand-carrier ladder after the rc80 genus-4 RiemannThetaG4: a numpy-free EXACT genus-5 theta-constant θ[ε';ε](0|Ω) held as an exact-integer (A₁..A₅, C₁₂,C₁₃,C₁₄,C₁₅,C₂₃,C₂₄,C₂₅,C₃₄,C₃₅,C₄₅) 15-TUPLE exponent lattice in the quarter-nome base (5 diagonal nomes + TEN cross-terms — vs genus-4's SIX; one per pair {12,13,14,15,23,24,25,34,35,45}, the genus-5 scaling difficulty). A pure CARRIER (the RiemannTheta / RiemannThetaG3 / RiemannThetaG4 / ThetaSum precedent): NO new ToolEntry → tools.total stays 341. ABI stays 3 (additive C symbols). Shipped CO-EQUAL Python + 1:1 native C peer in the SAME rc (the everything-mirrors / never-split discipline).
THE HEADLINE INVARIANT — 1024 = 2¹⁰ characteristics = 528 EVEN + 496 ODD (2^{g-1}(2^g±1) for g=5: even 16·33 = 528, odd 16·31 = 496; 528 + 496 = 1024 = 4^g; a characteristic is even iff ε'·ε ≡ 0 (mod 2)). RiemannThetaG5.even_null_count() → (528, 496) is PURE PARITY over the 1024 chars — cheap, NO lattice sum — and is the primary cheap assertion (the genus-5 lattice/duplication grows as (2·box+1)⁵: box 1 → 243, box 2 → 3125, box 3 → 16807, so the duplication square at box ≥ 2 is catastrophic; all lattice-touching tests are kept at box ≤ 2 and the duplication gate at box 1).
THE API. RiemannThetaG5(ep1..ep5, e1..e5) / theta_constant(eps_prime, eps) (10 binary characteristic bits); even_characteristics() (the 528 even nulls); is_even / genus (=5) / characteristic; lattice(box) (the exact-integer 15-tuple → coeff dict, native-dispatched); even_null_count() → (528,496) / singular_even_null(). The build gates (all exact, all PASS): (a) the genus axis — genus 5, 528 even + 496 odd; (b) the foundation-first exponent-lattice clearing — exact integer 15-tuple, all TEN C_ij = (2nᵢ+ε'ᵢ)(2nⱼ+ε'ⱼ) denominator-4 cross-terms; © COLLAPSE g5→g4 (primary): θ[0⁵;0⁵].collapse_g4() == the rc80 genus-4 trivial RiemannThetaG4 BIT-EXACT, AND collapse_g4_lattice_matches proves it derives from the lattice n₅=0 slice (not a hardcoded return); the all-trivial chain → genus-1 θ₃; a non-trivial 5th component HONESTLY REFUSES; (d) the FORMAL genus-5 Gauss/duplication identity θ[0;0](0|Ω)² = Σ_{c∈(½ℤ⁵/ℤ⁵)} θ[c;0](0|2Ω)² (32 summands) holds EXACTLY as a truncated exact-integer multivariate q-series for ALL Ω (no transcendental evaluation; DLMF §21.6.8 z=0 / all-zero specialization; Mumford, Tata Lectures on Theta I 1983), genuinely exercising all TEN cross-terms already at box 1; (e) no regression on the genus-⅔/4 gates. No float, no abs() (the (−1)^{ε·n} sign is the Class-K pin-slot), no numpy / math.
THE GENUINELY-OPEN genus-5 SCHOTTKY DECISION (the deliverable, NOT a deferred shell). RiemannThetaG5.schottky_g5_decision_is_open() returns the honest OPEN string. Unlike genus 4 — where the Jacobian locus J₄ ⊂ A₄ is a HYPERSURFACE cut by ONE weight-8 Schottky form J = θ⁴(E₈⊕E₈) − θ⁴(E₁₆) (the rc81 SchottkyFormG4 builds it exactly) — at genus 5 there is NO single modular form known to cut J_g, because none is known to exist: dim A₅ = g(g+1)/2 = 15, dim J₅ = dim M₅ = 3g−3 = 12, so J₅ has CODIMENSION 3 in A₅ (Grushevsky arXiv:1009.0369 Rmk 3.11) — it is NOT a hypersurface, so it CANNOT be the zero locus of a single form. Only an over-inclusive SUPERSET exists — the Andreotti–Mayer locus N_{g−4} = N₁ = {(A,Θ) : dim Sing Θ ≥ g−4 = 1} (of which J₅ is only an irreducible COMPONENT) and the Schottky–Jung locus (of which J₅ is again only an irreducible component) — plus the non-effective analytic KP / Shiota (Novikov-conjecture) characterization. So the genus-5 Jacobian-membership DECISION is operand-IRREPRESENTABLE — NOT because the carrier is unbuilt, but because the mathematics has no such form (a genuinely OPEN problem; Grushevsky Open Problem 1). The carrier BUILDS the exact genus-5 content but REFUSES to fabricate a verdict: NO is_jacobian / schottky_form_g5 / jacobian_verdict is built (the rc76/rc80 honest-OPEN pattern, now on a genuinely open problem). MPM-verified at build against the vendored Grushevsky, "The Schottky Problem", arXiv:1009.0369 (docs/srmech/hoodoos/): dim/codim counts (Rmk 3.11; §2 dim M_g = 3g−3, A_g = g(g+1)/2), the 2{g-1}(2g+1) even-theta count (§3), Open Problem 1 (g=5 genuinely open), Andreotti–Mayer Def 5.3 / Thm 5.4 N_{k,g} = {dim Sing Θ ≥ k} (AM67), Schottky–Jung Thm 4.4 (van Geemen 1984 / Donagi), and Shiota Thm 7.6 (KP/Novikov, Shiota 1986).
The C peer (everything-mirrors, same-rc). srmech_riemann_theta_g5_lattice (+ srmech_riemann_theta_g5_count) mirrors the genus-5 exact-integer exponent lattice — the [A1..A5,C12,C13,C14,C15,C23,C24,C25,C34,C35,C45,sign] 16-TUPLE lattice over a box (the genus-5 TEN cross-terms, each a denominator-4 clearing + the Class-K per-term sign) — over a caller arena (malloc-free, JPL Power-of-Ten clean: no goto/recursion; ≤60-line functions; ≥2 asserts/function; OVERFLOW-not-wrap; the (−1)^{ε·n} sign is a branch, never abs()). The Python RiemannThetaG5.lattice routes through it when the native symbol is present, trusting only a native hit; the pure-Python _lattice_py is the COMPLETE alternative + the parity oracle. Python == C byte-exact parity — the canonical 15-tuple → coeff lattice compared element-for-element across ALL 1024 characteristics (boxes 0/1) + a curated set at box 2; the C is NOT trusted — it is compared. Built + parity-verified under Release -DNDEBUG + -Werror pedantic (the CI-matching config).
[0.9.0rc85] - 2026-06-29¶
srmech.amsc.riemann_theta.RiemannThetaG4 gains the FULL Sp(8,ℤ) MODULAR-ACTION KIT — closing the genus-ladder gap so the g1→g4 modular-action ladder is UNIFORM. The genus carriers g2 RiemannTheta (Sp(4,ℤ)) and g3 RiemannThetaG3 (Sp(6,ℤ)) each carried a full modular-action kit (transform / sp{4,6}_{translation,gl_twist,inversion,compose,is_symplectic} / automorphy_factor / addition_holds / goepel_holds), but g4 RiemannThetaG4 had ONLY its lattice + collapse + duplication + Schottky-frontier surface — no transform / Sp(8) / automorphy / addition / Göpel. rc85 ports the ENTIRE g3 kit to g4 (the g=3→g=4 parametric extension; DLMF §21.5.9 / §21.6.8 hold for general genus g — here 4×4 blocks / 4-vectors over an 8×8 symplectic γ). A pure CARRIER extension (the g2/g3 *_holds precedent): NO new ToolEntry → tools.total stays 341. ABI stays 3 (additive C symbols). Shipped CO-EQUAL Python + 1:1 native C peers in the SAME rc (the everything-mirrors / never-split discipline).
THE Sp(8,ℤ) TRANSFORMATION. RiemannThetaG4.sp8_translation(B) ([[I,B],[0,I]], B symmetric 4×4), sp8_gl_twist(A) ([[A,0],[0,(Aᵀ)⁻¹]], A ∈ GL(4,ℤ), det = ±1 — exact unimodular inverse via the adjugate), sp8_inversion() ([[0,−I],[I,0]]), sp8_compose(g2,g1) (the block matrix product), sp8_is_symplectic(γ) (the exact integer block conditions AᵀC sym, BᵀD sym, AᵀD−CᵀB=I; the genus-4 J = [[0,−I],[I,0]]). transform(γ) → (RiemannThetaG4, k) — the EXACT genus-4 characteristic map ε' ↦ D·ε'−C·ε+diag(C·Dᵀ), ε ↦ −B·ε'+A·ε+diag(A·Bᵀ) (mod 2, bit-exact) + the 8th-root multiplier exponent k ∈ ℤ/8 (the exact Igusa phase 8·φ_m; the transcendental automorphy factor det(C·Ω+D)^{1/2} is OFF the decision path — carried SYMBOLICALLY by automorphy_factor(γ), never evaluated). Parity (even ⇄ even, odd ⇄ odd) is preserved by construction (the action factors through Sp(8,ℤ₂)).
THE GENUS-4 ADDITION + GÖPEL RELATIONS (the same goepel_holds capability g2/g3 expose). addition_holds(box=2) — the genuine two-argument genus-4 theta addition theorem (DLMF §21.6.8 at z₁=z₂=0, g=4 — the sum over r ∈ (ℤ/2)⁴, SIXTEEN terms) holds EXACTLY as a truncated exact-integer multivariate q-series for ALL Ω, exercising the genus-4 cross-terms C₁₄/C₂₄/C₃₄; addition_is_distinct_from_duplication is the no-shell proof (a product of two DISTINCT nulls, not a single squared null). goepel_holds(box=2) — the genus-4 UNIVERSAL Göpel quadratic theta-null relation Σ_{+} θ²[a]θ²[b] = Σ_{−} θ²[a]θ²[b], an 8-PAIR / 16-NULL same-Ω relation (the term-count grows with the genus: g2 is 3-pair/6-null, g3 is 4-pair/8-null; the genus-4 minimal sparse dependency is 8-pair, found over the [1,1,1,1;1,1,1,1] Göpel coset and re-verified bit-exactly as a q-series) among even theta-nulls all sharing the common GF(2) sum [1,1,1,1;1,1,1,1]; goepel_is_syzygous checks the azygetic-system fingerprint; goepel_is_distinct_from_duplication_and_addition the no-shell distinctness (degree-4 same-Ω vs degree-2 Ω-vs-2Ω). The genus-g Riemann theta relation among theta squares — Glass, Compositio Math. 40 (1980); Fiorentino–Salvati Manni, SIGMA 16 (2020) 057; Igusa, Theta Functions (1972) §IV/V; van der Geer, SMF Degree Two and Three. The transformation map + the addition theorem were MPM-verified against the genus-g forms (DLMF §21.5.9 / §21.6.8; Igusa, Theta Functions 1972; Mumford, Tata Lectures on Theta I 1983) — the SAME theorems one genus up, with the g3 source as the authoritative template.
THE BUILD GATES (all exact, all PASS). (A) every Sp(8,ℤ) generator + a composed product is symplectic; a deliberately non-symplectic 8×8 is rejected; the GL-twist (Aᵀ)⁻¹ is exact integer for unimodular A. (B) the characteristic action PRESERVES parity on all 256 characteristics for every generator; the group law composes exactly (transform(g₂·g₁) == transform(g₂)∘transform(g₁)); J⁴ acts trivially on all 256 chars; κ ∈ ℤ/8 exact (the translation gives k ∈ {0,4}). (C) transform RESTRICTS to the g3 sub-block — an embedded g3 generator's g4 action equals the g3 action on the first three bits + κ, with the 4th component staying trivial (the genus-ladder uniformity proof). (D) addition_holds(2) + goepel_holds(2) exact; both distinctness gates pass. (E) no regression on the rc75–78 g3 + rc80/81 g4 gates. The g2 RiemannTheta gains even_null_count() → (10,6) so all three genus carriers expose it uniformly.
The C peers (everything-mirrors, same-rc). srmech_riemann_theta_g4_sp8_char (the EXACT Sp(8,ℤ) characteristic transform + the κ 8th-root exponent over a 64-int64 γ = A,B,C,D 4×4 blocks; mirrors srmech_riemann_theta_g3_sp6_char), srmech_riemann_theta_g4_eighth_lattice (+ count; the COMMON eighth-nome genus-4 lattice at Ω / 2Ω the addition gate convolves), and srmech_riemann_theta_g4_goepel (+ count; the 8-pair / 16-null Göpel decision over the box-stable safe region — *out_holds = LHS==RHS, *out_has_cross = a genuine genus-4 cross-term present). Caller-arena (malloc-free, JPL Power-of-Ten clean: no goto/recursion; ≤60-line functions; ≥2 asserts/function; OVERFLOW-not-wrap; the Class-K per-term sign is a branch, never abs()). Python == C byte-exact parity (both paths driven + compared — the transform + κ on all 256 chars × all generators, the eighth-nome lattice at Ω and 2Ω over several boxes/chars, and the Göpel (holds, has_cross) decision; the C is NOT trusted — it is compared). The pure-Python bodies are the complete alternatives + the parity oracles.
[0.9.0rc84] - 2026-06-29¶
srmech.amsc.modular_forms_ring.ModularFormsRing — the level-1 ℂ[E₄,E₆] MODULAR-FORMS-RING carrier + its EXACT membership decision (the THIRD WEIGHT-axis rung). The third weight rung after the rc82 eta-quotient + rc83 Eisenstein: a new numpy-free carrier that makes the classical STRUCTURE THEOREM M_*(SL₂(ℤ)) = ℂ[E₄, E₆] EXECUTABLE — every level-1 weight-k holomorphic modular form is a UNIQUE exact-Q polynomial in E₄, E₆. Unlike the rc82/rc83 pure carriers, the ring's represent is a genuine REDUCER (q-series → exact closed form, or honest OPEN) — the WEIGHT-axis analog of the §76 Σ-row reducers (gosper / zeilberger / wz_certificate / dispatch.infer) — so it IS a ToolEntry: tools.total 340 → 341; the bare carrier constructor + the weight_monomials / dim accessors are NOT ToolEntries. ABI stays 3 (one additive C symbol). Shipped CO-EQUAL Python + 1:1 native C peer in the SAME rc (the everything-mirrors / never-split discipline).
THE API. ModularFormsRing.weight_monomials(k) — the weight-k monomial basis [(a, b): 4a+6b=k, a,b≥0] in canonical ascending-a order (e.g. weight_monomials(12) == [(0,2),(3,0)], weight_monomials(24) == [(0,4),(3,2),(6,0)]). ModularFormsRing.dim(k) — dim M_k = ⌊k/12⌋ + (0 if k≡2 (mod 12) else 1) for even k ≥ 0, 0 for odd k (and dim(k) == len(weight_monomials(k)) for every even k). ModularFormsRing.represent(q_series, k, *, n_terms=None) — THE MEMBERSHIP DECISION: solves the exact-Q system Σ_{a,b} c_{a,b}·(E₄^a E₆^b)[n] = q_series[n] over the monomial basis (built from the rc83 Eisenstein E₄/E₆ q-series + exact-Q truncated q-series multiplication), VERIFIES the solution reproduces ALL provided terms, and returns the unique exact-Q rep {(a,b): Q} — or None when no representation exists (the q-series is NOT a level-1 weight-k modular form within this carrier). The CONSTRUCTION/SOLVE is the decision (decompose-and-compute over the basis via the exact-Q QMat Gauss-Jordan — NOT a search). Requires ≥ dim(k)+2 terms (well-posed + verifiable). The module-level modular_forms_ring() (carrier ctor) + modular_forms_ring_represent(q_series, k) (the registered reducer) are the public helpers; a minimal ModularForm wrapper (weight + rep + q_series(n) reconstruction) names a single graded element. No float, no abs() (Class-K sign branch), no numpy / math. The structure theorem was MPM-verified at build against Serre, A Course in Arithmetic, GTM 7 (1973), Ch. VII §3 + Zagier, Elliptic Modular Forms and Their Applications (in The 1-2-3 of Modular Forms, Springer 2008), §2.2.
THE KEYSTONES (verified oracle = hardcoded known reps + the published rc82 Δ cross-check; construction IS the answer, no search; all PASS). (1) Δ = (E₄³−E₆²)/1728 @ weight 12 → {(3,0): Q(1,1728), (0,2): Q(-1,1728)} (the structure theorem: the cusp form Δ IS a polynomial in E₄,E₆; the Δ q-series oracle is cross-checked against the PUBLISHED rc82 EtaQuotient({1:24}) = η²⁴ Ramanujan τ — the carrier ladder validates itself). (2) E₈ @ 8 → {(2,0): Q(1)} (E₈ = E₄²). (3) E₁₀ @ 10 → {(1,1): Q(1)} (E₁₀ = E₄·E₆). (4) E₁₄ @ 14 → {(2,1): Q(1)} (E₁₄ = E₄²·E₆). (5) a non-modular q-series @ 12 → None (honest rejection). (6) dim / monomials: dim(12)=2, dim(8)=1, dim(4)=1, dim(24)=3, dim(0)=1; weight_monomials(12) == [(0,2),(3,0)]; dim(k) == len(weight_monomials(k)) for all even k tested. A genuine ℚ-combination (2·E₄³ + 3·E₆²) round-trips to its rep; the ModularForm wrapper reconstructs its q-series exactly.
THE REPRESENTABILITY CLOSURE (level 1) + the LEVEL-AXIS honest-OPEN. This carrier is the operand-side representability CLOSURE for level 1: because M_*(SL₂(ℤ)) is FULLY spanned by the E₄^a E₆^b monomials, represent is exact AND complete on level 1 — EVERY level-1 form is representable, non-forms → honest None. This is the CONTRAST with the eta-quotient / genus OPENs (representable forms with an irrepresentable membership decision); here the membership decision is itself exact + complete. The honest-OPEN is ONLY the LEVEL boundary: for N > 1, M_*(Γ₀(N)) is NOT ℂ[E₄,E₆] (it needs more generators), so a genuinely higher-level form (e.g. the X₀(11) weight-2 newform η²η²(11τ), correctly tested → None since M₂(SL₂(ℤ)) = {0}) lies OUTSIDE this carrier's ring — the boundary, not a bug; the next-theory is M_*(Γ₀(N)) (a # OPEN: note in the module).
The C peer (everything-mirrors, same-rc). srmech_modular_forms_ring_represent (+ srmech_modular_forms_ring_represent_ws_bound / srmech_modular_forms_ring_entry_cap) mirrors the membership solve over the caller-arena srmech_bigint substrate: it enumerates the weight-k monomial basis, builds each column E₄^a E₆^b from the rc83 srmech_eisenstein_qseries peer + an exact-Q truncated convolution, DISPATCHES the square leading-d-rows subsystem to the PUBLIC srmech_qmat_solve (exact Gauss-Jordan over bignum-Q — reuse, not reimplement), VERIFIES the candidate reproduces EVERY provided term, and returns the reduced (num, den) rep coefficients or a no-solution flag — covering the GENUINE rational case (Δ's 1/1728) with NO int64 ceiling. Caller-arena (malloc-free, JPL-clean: no goto/recursion; ≤60-line functions; ≥2 asserts/function; OVERFLOW-not-wrap; the Class-K sign is a branch, never abs()). Python == C byte-exact parity (both paths driven + the rep dict / None compared element-for-element on the keystones + a k=4..24 stress sweep; the C is NOT trusted — it is compared). The pure-Python body is the complete alternative + the parity oracle. Classified c_dispatched in the Rosetta ledger.
[0.9.0rc83] - 2026-06-29¶
srmech.amsc.eisenstein.Eisenstein — the SECOND WEIGHT-axis operand carrier (the normalized Eisenstein series E_k). The second weight rung after the rc82 eta-quotient: a new numpy-free EXACT-RATIONAL carrier on the WEIGHT axis (peer of EtaQuotient / UnaryTheta / RiemannTheta), E_k(τ) = 1 − (2k/B_k)·Σ_{n≥1} σ_{k−1}(n) qⁿ for even k ≥ 4, held as an EXACT-Q q-series modular form. A CARRIER, not a ToolEntry — tools.total is UNCHANGED (stays 340) (the Poly/QMat/EllRatio/ThetaSum/RiemannTheta/EtaQuotient carrier precedent); ABI stays 3 (one additive C symbol pair). Shipped CO-EQUAL Python + 1:1 native C peer in the SAME rc (the everything-mirrors / never-split discipline).
THE TWO GRADINGS. The carrier holds TWO gradings: the q-scale grading — the exact-Q q-series is the q-graded structure (the SAME scale structure the eta-quotient / elliptic carriers grade by); and the WEIGHT k — the modular AUTOMORPHY covariance, the (cτ+d)^k degree of E_k under τ → −1/τ (an EVEN INTEGER ≥ 4 for the level-1 holomorphic Eisenstein series). The carrier is STRUCTURAL-CONSTRUCT: the q-series + weight + leading power are COMPUTED from k exactly (Bernoulli recurrence + divisor power sum), NOT a solve. The coefficients are RATIONAL IN GENERAL — kept as exact Q, never collapsed to a float (the whole reason coeffs are Q, not int: E₁₂ has c₁ = 65520/691).
THE API. Eisenstein(k) (even int k ≥ 4; rejects odd k, k < 4, and k = 2 — the QUASIMODULAR boundary, named not built); .k / .weight (Q(k)) / .leading_power (Q(0) — constant term 1, holomorphic + non-vanishing at ∞, NOT a cusp form) / .prefactor (−2k/B_k, exact Q); .q_series(n_terms) (the exact-Q coefficients c_0 = 1, c_n = −(2k/B_k)·σ_{k−1}(n)); .is_modular() (True — every even k ≥ 4 level-1 E_k IS modular; anchors the k = 2 honest boundary at the constructor); equals / __eq__ / __repr__. Module-private helpers _bernoulli(k) (exact-Q via the standard recurrence Σ C(m+1,j)B_j = 0, B_0 = 1, integer binomials), _divisor_power_sum(e, n) (σ_e(n) = Σ_{d|n} d^e, exact int trial division), _binomial(n, r) (exact int). No float, no abs(), no numpy / math. The formula was MPM-verified at build against Serre, A Course in Arithmetic, GTM 7 (1973), Ch. VII §3 + Zagier, Elliptic Modular Forms and Their Applications (in The 1-2-3 of Modular Forms, Springer 2008), §2.2–2.3.
THE KEYSTONES (verified oracle = hardcoded known coeffs + the published rc82 cross-check; construction IS the answer, no search; all PASS). (1) Eisenstein(4).q_series(5) == [1, 240, 2160, 6720, 17520] (240·σ₃), weight Q(4). (2) Eisenstein(6).q_series(5) == [1, −504, −16632, −122976, −532728] (−504·σ₅), weight Q(6). (3) Eisenstein(8).q_series(6) == [1, 480, 61920, 1050240, 7926240, 37500480] (480·σ₇). (4) THE HEADLINE CROSS-RUNG: E₄³ − E₆² == 1728·Δ == 1728·η²⁴ — E₄³−E₆² as an exact-Q convolution has [0] == 0 (a CUSP form) and [n] == 1728·τ(n); verified against the PUBLISHED rc82 carrier EtaQuotient({1:24}): (E₄³−E₆²)[1:]/1728 == EtaQuotient({1:24}).q_series(N)[:N−1] (== Ramanujan τ = [1,−24,252,−1472,4830,−6048,−16744,…]). The carrier ladder validates itself. (5) Ring identities: E₄² == E₈ (M₈ is 1-dim) and E₄·E₆ == E₁₀. (6) The exact-rational keystone: Eisenstein(12).q_series(2)[1] == Q(65520, 691) (proves the carrier handles genuine non-integer rational coeffs).
E_k GENERATES THE RING + THE OPERAND-IRREPRESENTABLE BOUNDARY (the honest OPENs). E₄, E₆ generate the WHOLE graded ring M_*(SL₂(ℤ)) = ℂ[E₄, E₆] (Serre 1973 / Zagier 2008); the rc84 ring rung will build the polynomial-in-(E₄, E₆) decomposition ON this carrier. The honest-OPENs (the operand-side dual of the genus-axis Schottky / eta-quotient-subspace OPENs — representable here, named-open beyond): (1) the k = 2 QUASIMODULAR boundary — E₂ is NOT modular (M₂(SL₂(ℤ)) = {0}; E₂ picks up a non-holomorphic correction under τ → −1/τ); the constructor REJECTS k = 2 and names the next-theory, the quasimodular ring ℂ[E₂, E₄, E₆] (NOT built). (2) the LEVEL boundary — this is the LEVEL-1 (SL₂(ℤ)) carrier; higher-level / nebentypus Eisenstein series E_{k,χ} on Γ₀(N) (Diamond & Shurman, A First Course in Modular Forms, GTM 228 (2005), §4.5–4.8) are the boundary (NOT built). Both are # OPEN: notes in the module.
The C peer (everything-mirrors, same-rc — FULL rational scope). srmech_eisenstein_qseries (+ srmech_eisenstein_ws_bound) mirrors the compute kernel — the EXACT-RATIONAL coefficient list — over the caller-arena srmech_bigint substrate (the same exact substrate as srmech_poly / srmech_eta_quotient, here carrying reduced num/den pairs). It computes B_k as an exact rational by the Bernoulli recurrence over a caller-arena Bernoulli rational roster, σ_{k−1}(n) as an exact integer (divisor-pair trial division + srmech_bigint_pow_u32), and the reduced rational coefficient c_n = −(2k/B_k)·σ_{k−1}(n) (gcd-reduce, positive denominator) — covering the GENUINE rational case (k = 12 → 65520/691), NOT just the integer-coeff weights (the difficulty of Bernoulli-in-C is not an exemption; a partial integer-only shell would violate the no-partial-ship discipline). Only bigint add/sub/mul/divmod/gcd/pow; the sign is the Class-K pin-slot, never an ALU abs(). Caller-arena (malloc-free, JPL-clean: no goto/recursion; ≤60-line functions; ≥2 asserts/function; OVERFLOW-not-wrap). The is_modular / quasimodular-boundary DECISION stays Python-only (the carrier precedent). Python == C byte-exact parity (both paths driven + compared num/den element-for-element; the C is NOT trusted — it is compared). The pure-Python body is the complete alternative + the parity oracle.
[0.9.0rc82] - 2026-06-29¶
srmech.amsc.eta_quotient.EtaQuotient — a WEIGHT-axis operand carrier (the Dedekind-eta quotient). A new numpy-free EXACT carrier on the WEIGHT axis (peer of UnaryTheta / the harmonic-Maass + RiemannTheta genus carriers): Q(τ) = ∏_{d|N} η(dτ)^{r_d}, a Dedekind-eta quotient held as an EXACT q-series modular object. A CARRIER, not a ToolEntry — tools.total is UNCHANGED (the Poly/QMat/EllRatio/ThetaSum/RiemannTheta carrier precedent); ABI stays 3 (one additive C symbol pair). Shipped CO-EQUAL Python + 1:1 native C peer in the SAME rc (the everything-mirrors / never-split discipline).
THE TWO GRADINGS. The carrier holds TWO distinct gradings: the q-scale grading — the exact q-series ∏_d ∏_{m≥1}(1−q^{dm})^{r_d} is the q-graded structure (the SAME scale structure the elliptic carrier's qshift σ grades by); and the WEIGHT k = ½ Σ_d r_d — the modular AUTOMORPHY covariance, the (cτ+d)^k degree of Q under τ → −1/τ (a half-integer in general → exact Q, the natural home for the weight axis). The carrier is STRUCTURAL-CONSTRUCT: the q-series + weight + leading power are COMPUTED from the exponents exactly; modularity is DECIDED by exact integer congruences — NOT a solve.
THE API. EtaQuotient({d: r_d}) (canonicalises: sorts, drops zeros; rejects empty / non-positive d); .exponents / .level (N = lcm(d), the Γ₀(N) level) / .weight (Q(Σ r_d, 2)) / .leading_power (Q(Σ d·r_d, 24), the q-valuation / order at ∞); .q_series(n_terms) (the EXACT integer coefficients — they GROW, e.g. the Ramanujan τ, kept exact with no int64 ceiling; a negative r_d rides the geometric 1/(1−x) expansion, the Class-K sign branch); .is_modular() (the exact-integer LIGOZAT decision: Σ_d d·r_d ≡ 0 (mod 24) AND Σ_d (N/d)·r_d ≡ 0 (mod 24)); .order_at_cusp(c) for c|N (the standard Ligozat order-at-cusp formula (N/24)·Σ_d gcd(c,d)²·r_d/(gcd(c,N/c)·c·d), exact Q, Class-K sign) + .is_holomorphic() (is_modular() AND every cusp order ≥ 0). All integer arithmetic, no float, no abs(), no numpy / math. The Ligozat criterion + order-at-cusp formula were MPM-verified at build against Ono, The Web of Modularity: Arithmetic of the Coefficients of Modular Forms and q-Series, CBMS 102 (2004), Theorem 1.64 / 1.65 (+ Rouse–Webb, On spaces of modular forms spanned by eta-quotients, Adv. Math. 272 (2015)).
THE KEYSTONES (verified oracle; construction IS the answer, no search; all PASS). (1) η²⁴ = Δ: EtaQuotient({1: 24}) → weight 12, level 1, leading power 1, q_series(8)[:7] == [1, −24, 252, −1472, 4830, −6048, −16744] (Ramanujan τ(1..7), OEIS A000594); is_modular() == True; is_holomorphic() == True (the weight-12 cusp form on Γ(1); cusp order at ∞ = 1). (2) the X₀(11) weight-2 newform: EtaQuotient({1: 2, 11: 2}) = η(τ)²η(11τ)² → weight 2, level 11, leading power 1, q_series(8)[:7] == [1, −2, −1, 2, 1, 2, −2] (the elliptic-curve newform a(1..7); LMFDB 11.2.a.a); is_modular() == True (both cusp orders = 1 ≥ 0). The test oracle = the hardcoded known τ / newform coefficients (NOT the carrier re-running itself).
THE OPERAND-IRREPRESENTABLE BOUNDARY (the honest OPEN). The carrier REPRESENTS eta-quotient modular forms exactly + DECIDES the modularity of a GIVEN exponent vector exactly. But the eta-quotient subspace of M_k(Γ₀(N)) is PROPER (Rouse–Webb 2015): not every weight-k form is an eta-quotient. So deciding "is a GIVEN q-series an eta-quotient?" is the honest-OPEN — a search over exponent vectors, no finite closed cutter in general — the operand-IRREPRESENTABLE boundary, DUAL to the genus-axis Schottky membership decision (a representable FORM whose Jacobian-membership DECISION is irrepresentable; the SchottkyFormG4 precedent). NO general is-eta-quotient solver is built (a # OPEN: note in the module).
The C peer (everything-mirrors, same-rc). srmech_eta_quotient_qseries (+ srmech_eta_quotient_ws_bound) mirrors the compute kernel — the EXACT INTEGER product expansion ∏_d ∏_{m≥1}(1−q^{dm})^{r_d}, built factor by factor over the caller-arena srmech_bigint substrate (the same exact-integer substrate as srmech_poly / srmech_unary_theta; the coefficients grow with NO int64 ceiling). r_d > 0 is a backward subtract-shift out[i] −= out[i−e], r_d < 0 a forward add-shift out[i] += out[i−e] (the geometric 1/(1−q^e); the Class-K sign branch chooses, never an ALU abs()). Only bigint add/sub — ONE scratch bigint, caller-arena, malloc-free + JPL-clean (no goto/recursion; ≤60-line functions; ≥2 asserts/function). The Ligozat / order-at-cusp DECISION logic stays Python-only (the carrier precedent: the q-series expansion is the compute kernel that needs the C peer). Python == C byte-exact parity (both paths driven + compared element-for-element). The pure-Python body is the complete alternative + the parity oracle.
[0.9.0rc81] - 2026-06-28¶
srmech.amsc.riemann_theta.SchottkyFormG4 — the genus-4 SCHOTTKY FORM J: the GENUS-4 CAPSTONE. rc80 built the genus-4 RiemannThetaG4 carrier (the theta-CONSTANT) and DOCUMENTED the Schottky frontier as the operand-side OPEN, naming the Schottky form J as the rc81 capstone. rc81 BUILDS J — the χ₁₈-analog at g = 4, the weight-8 degree-4 level-1 Siegel CUSP form whose vanishing cuts the genus-4 Jacobian locus J₄ ⊂ A₄ (the Schottky problem's g = 4 solution: Schottky 1888 / Igusa 1981 / Poor–Yuen 1996). A NEW class SchottkyFormG4 on the riemann_theta module (the rc72–rc80 genus-⅔/4 theta-constant surfaces UNTOUCHED — zero regression). All new surfaces are CARRIER METHODS / classmethods (the carrier precedent), so tools.total is UNCHANGED; ABI stays 3 (additive C symbols). Shipped CO-EQUAL Python + 1:1 native C peer in the SAME rc (the everything-mirrors / never-split discipline).
THE EXACT CONSTRUCTION (the lattice-theta-difference route). J ∝ θ_{E₈⊕E₈}^{(4)}(Ω) − θ_{E₁₆}^{(4)}(Ω) — the DIFFERENCE of the genus-4 theta-SERIES of the TWO rank-16 even-unimodular lattices E₈⊕E₈ and E₁₆ = D₁₆⁺ (Wikipedia "Schottky form" + Igusa 1981 J ∝ θ⁴(E₈⊕E₈) − θ⁴(E₁₆), MPM-verified at build; Poor & Yuen 1996 — J spans the 1-dim level-1 genus-4 weight-8 cusp space). Both are weight-rank/2 = 8 Siegel modular forms; by Witt (1941) the two genus-g lattice theta-series are EQUAL for g ≤ 3 and FIRST DIFFER at g = 4, so their difference is the NONZERO Schottky cusp form J (the first-genus-4 obstruction). The lattices (Conway–Sloane SPLAG): E₈ = {ℤ⁸ ∪ (ℤ+½)⁸, even coordinate sum} (240 roots); E₁₆ = D₁₆⁺ = {ℤ¹⁶ ∪ (ℤ+½)¹⁶, even coordinate sum} (480 roots) — both even (⟨v,v⟩ ∈ 2ℤ) and unimodular (E₈ via its Cartan-matrix Gram — even diagonal, det 1; D₁₆⁺ via the index argument det = det(D₁₆)/[D₁₆⁺:D₁₆]² = 4/4 = 1). MPM-source for the Gram matrices verified at build (NOT hallucinated).
THE EXACT q-SERIES (no float; representation-number organized). θ_L^{(g)}(Ω) = Σ_{(v₁..v_g)∈L^g} exp(iπ Σ_{i,j} ⟨vᵢ,vⱼ⟩ Ωᵢⱼ), organized by the GRAM matrix T_ij = ⟨vᵢ,vⱼ⟩ of the g-tuple: the coefficient at q^T is the EXACT INTEGER representation number r_L(T) = #{g-tuples with Gram T}, so J's coefficient at T is r_{E₈⊕E₈}(T) − r_{E₁₆}(T) — a numpy-free integer count, never a float. The carrier works in the DOUBLED-vector integer model (real coords ×2, so half-integer coords are EXACT odd integers; the doubled inner ⟨2vᵢ,2vⱼ⟩ = 4⟨vᵢ,vⱼ⟩ IS the C_ij quarter-nome exponent, consistent with the rc80 RiemannThetaG4 base). J's EXACT, finite, load-bearing part is the MINIMAL SHELL (all g vectors minimal/norm-2; the leading part of the cusp form — the rc76 χ₁₈ leading-part pattern).
THE DEFINING SCHOTTKY GATE (gorgeous, no-shell — the first-genus-4 obstruction; all PASS, Python and C). (1) J computed EXACTLY as a formal q-series (exact-integer, numpy-free) and NONZERO at genus 4. (2) THE DEFINING GATE: J VANISHES identically below genus 4 — the genus-1, genus-2 AND genus-3 minimal-shell theta-series of E₈⊕E₈ and E₁₆ are EXACTLY EQUAL (collapses_below_genus4() == True; every Gram's representation number agrees — Witt 1941 made executable; J_minimal(1/2/3) == {}), so J|_{g≤3} ≡ 0 (which is ALSO why J is a CUSP form: the Siegel Φ-operator kills it) — WHILE J|_{g=4} ≠ 0 (is_nonzero_at_genus4() == True). The genus-4 first difference is EXACT: at the orthogonal frame T = 2·I₄ (four mutually-orthogonal norm-2 vectors) r_{E₈⊕E₈} − r_{E₁₆} = 9 064 742 400 − 8 858 304 000 = 206 438 400 ≠ 0 (the famous first difference; first_difference_orthogonal_frame()); the fast pure-Python certificate is the D₄-star Gram 7 257 600 − 2 096 640 = 5 160 960 ≠ 0. (3) Weight-8 degree-4 cusp structure: weight() == 8 (= rank/2), degree() == 4, is_cusp_form_structure() == True (Φ(J) = 0), cusp_space_dimension() == 1 (Poor & Yuen 1996, J spans S₈(Γ₄)). (4) HONEST OPEN preserved: the numerical "is THIS Ω a Jacobian" decision (J(Ω) = 0 at a transcendental Ω ∈ H₄) STAYS the operand-side OPEN — jacobian_decision_is_open() returns the honest string (the rc80 schottky_locus_is_open pattern, upgraded to reference the BUILT J); NO numerical Jacobian decision is built (is_jacobian / jacobian_verdict do NOT exist). (5) No regression: ALL rc72–rc80 genus-⅔/4 gates still PASS exactly. (6) Python == C parity EXACT on J's construction (the count + shell peers vs the pure oracle, byte-identical) + ratchet-clean (no numpy / math / abs / float on any decision path) + published-wheel verify (numpy-absent venv outside source tree).
The genus-4 set is now COMPLETE (rc80 RiemannThetaG4 carrier → rc81 SchottkyFormG4 capstone). The g ≥ 5 frontier (the Schottky problem genuinely OPEN for g ≥ 5; no single modular form is known to cut J_g) is the documented operand-side OPEN beyond this rung.
The C peer (everything-mirrors, same-rc). srmech_riemann_theta_g4_schottky_count (a single prescribed off-Gram count, the gates' workhorse) + srmech_riemann_theta_g4_schottky_shell (the full single-pass off-Gram histogram, the genus-≤3 agreement engine) + their arena/size helpers mirror the heavy exact-integer minimal-shell g-tuple representation count — a malloc-free, caller-arena, JPL-clean bitset count of ordered g-tuples of minimal (doubled) vectors with a prescribed doubled-Gram (the Class-L adjacency-by-inner-value walk; a pure non-negative integer tally, NO abs()). The Python body is the complete alternative + parity oracle (byte-identical). ABI-additive (stays 3).
[0.9.0rc80] - 2026-06-28¶
srmech.amsc.riemann_theta — the genus-4 RiemannThetaG4 carrier: the NEXT GENUS RUNG, RESUMING the genus axis into the SCHOTTKY FRONTIER. rc72–rc74 built the genus-2 Riemann theta-CONSTANT (the first rung) + its modular action + the Thomae/Rosenhain capstone; rc75–rc78 built the genus-3 RiemannThetaG3 rung-set (carrier → χ₁₈ → transform+addition → Göpel syzygy). rc80 climbs the GENUS axis one rung to genus 4 — a numpy-free EXACT genus-4 Riemann theta-constant θ[ε'; ε](0 | Ω) over a 4×4 SYMMETRIC Ω ∈ H₄ (the Siegel upper half space, dim g(g+1)/2 = 10) — the genus-4 analog of the rc75 genus-3 first rung, extending the riemann_theta module with a NEW class RiemannThetaG4 (a genus-4-specific extension; the rc72–rc78 genus-2/genus-3 surfaces are UNTOUCHED — zero regression). All new surfaces are CARRIER METHODS / classmethods (the rc72–rc78 carrier precedent), so tools.total is UNCHANGED; ABI stays 3 (two additive C symbols). Shipped CO-EQUAL Python + 1:1 native C peer in the SAME rc (the everything-mirrors / never-split discipline).
THE OBJECT. Binary characteristic [ε'; ε], ε', ε ∈ {0,1}⁴ → 256 total characteristics = 136 EVEN + 120 ODD (2^{g-1}(2^g±1) for g = 4: even 8·17 = 136, odd 8·15 = 120; 136 + 120 = 256 = 4^g, MPM-verified arithmetic; the empty-set even null [0,0,0,0;0,0,0,0] is the distinguished singular one; Grushevsky, The Schottky Problem, arXiv:1009.0369). A characteristic is even iff ε'·ε ≡ 0 (mod 2).
THE EXACT NOME-LATTICE (no float on the decision path; THE GENUS-4 SCALING DIFFICULTY = SIX cross-terms). The carrier represents the theta-CONSTANT as an EXACT INTEGER exponent lattice over 4 diagonal nomes qᵢ = e^{iπΩᵢᵢ} + SIX CROSS-TERMS q₁₂, q₁₃, q₁₄, q₂₃, q₂₄, q₃₄ = e^{2iπΩᵢⱼ} (vs genus-3's THREE — this is the genuinely-new genus-4 content; one per pair {12,13,14,23,24,34}). With mᵢ = nᵢ + ½ε'ᵢ, cleared to the quarter-nome base Qᵢ = qᵢ^{1/4} / Q_ij = q_ij^{1/4}, a lattice point is a 10-index TUPLE (A₁,A₂,A₃,A₄,C₁₂,C₁₃,C₁₄,C₂₃,C₂₄,C₃₄) with EXACT INTEGER exponents Aᵢ = (2nᵢ+ε'ᵢ)² and C_ij = (2nᵢ+ε'ᵢ)(2nⱼ+ε'ⱼ) — each cross-term a PRODUCT of two half-integers → a denominator-4 integer-lattice clearing, now across SIX coupled pairs. Box truncation |nᵢ| ≤ box → (2·box+1)⁴ monomial terms (kept SMALL — box ⅔ — since (2N+1)⁴ grows fast; the formal relations are box-stable); each coefficient is an exact integer (a sum of ±1 lattice counts); the sign (−1)^{ε·n} is the Class-K pin-slot (an explicit ±1 branch, never an ALU abs()). FOUNDATION-FIRST: the 4-index exponent-lattice CLEARING (all 256 characteristics, the 6 cross-terms, denominator-4 handling) was built + verified as its own unit FIRST, gated on the collapse, before the formal relation.
The exact gates (no-float, no-shell; all PASS, Python and C). (1) Collapse g4→g3 (THE foundation gate): θ[0,0,0,0;0,0,0,0].collapse_g3() collapses EXACTLY to the rc75 genus-3 trivial RiemannThetaG3 (set n₄=0, q₄=q₁₄=q₂₄=q₃₄=1, ε'₄=ε₄=0) — bit-exact vs the existing rung AND DERIVED from the lattice n₄=0 slice (collapse_g3_lattice_matches(box) == True, box-stable), not a hardcoded return; the all-trivial chain → genus-3 → genus-2 → genus-1 θ₃ (collapse_g1_q_series → [1,2,0,0,2,…]). A characteristic with a NON-trivial 4th component HONESTLY REFUSES to collapse (raises — the rc75 collapse pattern, an honest boundary, not a fabricated reduction). (2) Formal genus-4 theta-null identity: the genus-4 Gauss / DUPLICATION identity θ[0;0](0|Ω)² = Σ_{c ∈ (½ℤ⁴/ℤ⁴)} θ[c;0](0|2Ω)² (16 summands) holds EXACTLY as a truncated exact-integer multivariate q-series for ALL Ω — the z₁=z₂=0, all-zero-characteristic specialization of the DLMF §21.6 addition formula with characteristics (DLMF eq. 21.6.8, re-verified at build at https://dlmf.nist.gov/21.6: setting α=β=γ=δ=0 and z₁=z₂=0 collapses the two factors to one square and the sum to the 2^g half-characteristics c = ½ν, ν ∈ {0,1}^g; classically Mumford, Tata Lectures on Theta I (1983), the genus-g duplication; Chai, "Riemann's theta formula" (2014), Thm 1.2 example (b)). The sixteen θ[c;0] include the (½,½,½,½) + mixed characteristics with C₁₄ ≠ 0/C₂₄ ≠ 0/C₃₄ ≠ 0, so the identity genuinely exercises ALL SIX cross-terms — proving genuine genus-4 theta-constants, not the genus-3 slice (duplication_holds(2) == True). (3) No regression: ALL rc72/73/74 genus-2 + rc75/76/77/78 genus-3 gates still PASS exactly (collapse_g2, duplication_holds, even_null_count, chi18_, sp6_, addition, goepel_). *(4) Python == C parity EXACT** on the genus-4 lattice — ALL 256 characteristics × 4 boxes = 1024 checks, 0 mismatches (the 6-cross-term exact clearing, Python == C on all 256 chars) + the gates through the native peer. (5) ratchet-clean (no numpy / math / abs / float on any decision path; AST + JPL audit + tool-schema-coverage + rosetta + rotation-last). (6) published-wheel verify (numpy-absent venv outside source tree).
THE SCHOTTKY FRONTIER (genus 4 turns it ON — the documented operand-side OPEN; J is the rc81 capstone, NOT this rc). Genus 4 is the FIRST genus where the Jacobian locus J₄ is a PROPER subvariety of A₄ (dim M₄ = 3g−3 = 9 < dim A₄ = g(g+1)/2 = 10) — the SCHOTTKY problem turns on (unlike g ≤ 3 where J_g = A_g^ind is everything). The cutter is the Schottky form J — a weight-8 degree-4 Siegel cusp form, Schottky's (1888) degree-16 polynomial in the 136 even theta-nulls; Igusa (1981): J ∝ θ⁴(E₈⊕E₈) − θ⁴(E₁₆), the difference of the genus-4 theta-series of the two rank-16 even-unimodular lattices (which AGREE for g ≤ 3 and first DIFFER at g = 4), with irreducible divisor; Poor & Yuen (1996): J spans the 1-dimensional level-1 genus-4 weight-8 cusp-form space (MPM-verified at build from the Wikipedia "Schottky form" article + Grushevsky arXiv:1009.0369). J vanishes exactly on J₄. The NUMERICAL "is THIS Ω a Jacobian" decision is a POINT-EVALUATION J(Ω) = 0 at a transcendental Ω ∈ H₄ (only knowable to N digits = float on the decision path) → NOT a finite exact carrier op → the operand-side OPEN: schottky_locus_is_open() returns the honest OPEN string (the rc76 hyperelliptic_locus_is_open pattern). rc80 BUILDS the exact carrier content (the lattice, the g4→g3 collapse, the genus-4 duplication, the 136-even / 120-odd enumeration, the singular even null) but DOCUMENTS — does not build — J (is_jacobian / schottky_form do NOT exist). The exact formal-q-series J (via the E₈⊕E₈ − E₁₆ lattice-theta difference) is the rc81 capstone, with the numerical decision the documented OPEN. (Schottky frontier: g = 4 ON, solved by Schottky; g ≥ 5 genuinely OPEN.)
The C peer (everything-mirrors, same-rc). srmech_riemann_theta_g4_lattice (+ srmech_riemann_theta_g4_count) mirrors the genuinely-new exact-integer kernel — the genus-4 (A₁,A₂,A₃,A₄,C₁₂,C₁₃,C₁₄,C₂₃,C₂₄,C₃₄) exponent lattice with the SIX cross-terms' denominator-4 clearing + the per-term Class-K sign, emitted as flat caller-owned int64 [A1,A2,A3,A4,C12,C13,C14,C23,C24,C34,sign] 11-tuples (theta-CONSTANT coefficients are small ±1 lattice counts → int64-exact, no bignum). Caller-arena (malloc-free, JPL-clean, no goto/abs); the Python body is its complete alternative + parity oracle (byte-identical). ABI-additive (stays 3).
[0.9.0rc79] - 2026-06-28¶
srmech.rbs_lm.RBSLMInferenceSubstrate.next_token_coherence — the native RAW collapse-margin + COHERENT/BRANCH/STOP coherence trichotomy (closes UPSTREAM §78; F943/F944/F945). The downstream RBS-LM research wired a per-step coherence readout out of sub.M / sub.ctx / sub.vocab_vecs because next_token_distribution returns ONLY the softmaxed probs — whose top₁ − top₂ is FLATTENED by the full-vocab softmax (it read 0.006 on a confidently-resolving step → a FALSE honest-stop, F944). The true collapse-margin is the RAW-sim gap (top₁ − top₂ of klein4_similarity BEFORE the softmax). rc79 makes the readout native: a new method + a frozen CoherenceReadout dataclass on srmech.rbs_lm (NOT a module-level callable, so the tool-schema coverage — which walks only srmech.amsc.* / srmech.qm.* functions — is untouched; tools.total UNCHANGED).
The trichotomy (F945, the branching/general case). With the raw sims + a principled noise floor, a LOW margin splits THREE ways: COHERENT (top₁ ≫ floor, margin high → emit the one next); BRANCH (top₁ AND top₂ both ≥ floor, margin low → a LEGITIMATE multi-next choice point — sample among the valid hands, NOT an error); STOP (top₁ ≈/below floor → incoherent/noise → honest-stop). It is the recall-level form of the §77/F934 "verified or honest-OPEN" contract, one layer down; pairs with community-tome routing (F778/F465) which keeps single-next margins high.
The no-magic noise floor. The floor is built from STRUCTURE, never a hardcoded 0.34: baseline = the random-Klein-4 match probability = Q(1, 4) (4 symbols {0,1,2,3} agree at a position with probability exactly ¼ — Class-A attestation to the alphabet) PLUS a documented band Q(9, 100) (the F945 measured operating floor was 0.34 = 0.25 + 0.09; the band sits safely in the gap between the ~0.25 chance level and the lowest genuine recall). So NOISE_FLOOR_Q = Q(1, 4) + Q(9, 100) = Q(17, 50) — the ≈ 0.34 floor reduced to its source, de-magicked. The branch-margin threshold default is BRANCH_BAND_Q = Q(3, 25) (the F945 m < 0.12 test). All three (noise_floor / noise_band / branch_band) are parameters with the attested defaults.
Decision path is exact-Q end to end (F868 stay-rational). The readout ranks on Q(klein4_match_count(probe, c), D) (the exact rational sim, the same integer match-count the float sim_k4_batch divides for the hot path) — the sort/argmax is Q.__lt__ (exact integer cross-multiply = Class-K pin-slot compare, NO float sort key), and the floor/margin/gap compares stay rational, collapsing to a decimal only if a consumer opts in via float(). NO numpy / import math / abs() / float on the decision path.
Non-breaking + no new C owed. next_token_distribution is UNCHANGED (byte-for-byte; a regression test pins it). The inference layer is Python orchestration over the C-backed Class-M kernels (klein4_bind / klein4_match_count are native-dispatched) — next_token_coherence adds NO new heavy compute, so it owes NO new C kernel (it matches next_token_distribution being Python-only orchestration; everything-mirrors is honored — the primitives it composes are already C-mirrored). ABI stays 3.
The exact gates (all PASS). (1) F945 trichotomy reproduced through the substrate's own probe on the finding's graph (a→b, a→c branch, b→d, c→d merge, d→e), routed by source into bounded tomes (community-tome routing, the finding's title): recall a = BRANCH (b,c both ~0.566 ≥ floor 0.34, margin ~0.002, two valid hands {b,c}); recall b/c/d = COHERENT (margin ~0.742, one next); a pure-noise context = STOP (top₁ ~0.259 < floor). (2) raw ≠ softmax-flattened: on the COHERENT d→e step the raw collapse_margin (~0.742) is strictly larger than the softmaxed next_token_distribution top1−top2 gap (~0.10 at T=1.0 — the F944 false-stop the ask fixes). (3) No regression: the full rbs_lm suite + the whole suite green; next_token_distribution output unchanged. (4) ratchet-clean (AST no-numpy / no-import-math / no-float-on-decision-path; no C touched beyond the version bump). (5) published-wheel verify (numpy-absent venv outside source tree: next_token_coherence present + BRANCH/COHERENT/STOP reproduced through the published wheel).
[0.9.0rc78] - 2026-06-28¶
srmech.amsc.riemann_theta — the genus-3 GÖPEL / FROBENIUS theta-null quadratic SYZYGY (closes the genus-3 rung-set). rc74 built the genus-2 Göpel quadratic syzygy on RiemannTheta (θ²[a]θ²[b] = θ²[c]θ²[d] − θ²[e]θ²[f], six distinct even nulls forming a Göpel system). rc78 is the genus-3 analog on the rc75/76/77 RiemannThetaG3 carrier — the LAST rung of the genus-3 set: carrier (rc75) → χ₁₈ (rc76) → transform + addition (rc77) → syzygy (rc78). All new surfaces are CARRIER METHODS / classmethods (the rc72–rc77 carrier precedent), so tools.total is UNCHANGED (340); ABI stays 3 (two additive C symbols). Shipped CO-EQUAL Python + 1:1 native C peer in the SAME rc (the everything-mirrors / never-split discipline).
The genus-3 syzygy (the genuine exact content — and its genus-2 difference). The relation is a 4-PAIR / 8-NULL same-Ω quadratic identity among even theta-NULLS:
θ²[000;001]·θ²[111;110] = θ²[000;010]·θ²[111;101] + θ²[001;000]·θ²[110;111] − θ²[010;000]·θ²[101;111]
— the eight DISTINCT even nulls forming four pairs that ALL sum to the common GF(2) characteristic [1,1,1; 1,1,1] (a genus-3 GÖPEL / azygetic system). THE GENUS-3 SHAPE IS GENUINELY DIFFERENT FROM GENUS 2 (not a stylistic choice): the rc74 genus-2 relation is 3-pair / 6-null, but the genus-2-style 6-null lift does NOT hold for genus 3 — an EXHAUSTIVE search over all 63 GF(2)-sum classes × all 6-null common-sum triples finds NO 3-term genus-3 relation; the MINIMAL common-sum relation among the genus-3 θ²[m]θ²[m+s] products is 4-term (the nullspace's sparsest dependency, coefficients ±1). The extra term is the genuine genus-3 content (the third lattice direction's cross-terms C₁₃, C₂₃ couple in). It holds for ALL Ω, checked EXACT as a truncated exact-ℚ multivariate q-series (no transcendental evaluation, no float). MPM source: J. P. Glass, "Theta constants of genus three", Compositio Mathematica 40 (1980) §3 — the degree-4 relations among the 36 even genus-3 theta-constants partition into three basic types with coefficients ±1, type (2) = "products of squares of theta constants" (this syzygy); A. Fiorentino & R. Salvati Manni, "On Frobenius' Theta Formula", SIGMA 16 (2020) 057 §1–2 (the azygetic Göpel structure + the biquadratic Riemann relations); Igusa, Theta Functions (1972) §IV/V; van der Geer, Siegel Modular Forms of Degree Two and Three.
Distinctness (no-shell, the critical gate). goepel_is_distinct_from_duplication_addition_and_chi18() PROVES (structural + exact-lattice) the syzygy is GENUINELY DISTINCT from all three prior genus-3 relations: vs rc75 duplication (Ω-vs-2Ω, single null squared, degree-2) and rc77 addition (Ω-vs-2Ω, two-argument, degree-2) — the Göpel LHS is a degree-4 same-Ω product with NO Ω-doubling, unequal to every duplication/addition LHS on the safe region; vs rc76 χ₁₈ — the 8 syzygy nulls are a PROPER SUBSET of the 36 χ₁₈ factors (8 < 36) and the syzygy is a same-Ω SUM, not the 36-null product.
The exact gates (no-float, no-shell; all PASS, Python and C). (1) genus-3 Göpel syzygy holds: goepel_holds(box) == True (exact-ℚ, all Ω; box-stable — a fixed inner region is IDENTICAL across box = 3, 4, 5; residual vanishes at box 5; the region is non-trivially populated with genuine genus-3 C₁₃/C₂₃ cross-terms); goepel_is_syzygous() == True (8 distinct even nulls, four pairs, one common GF(2) sum). (2) distinctness: proven distinct from duplication + addition + χ₁₈. (3) No regression: ALL rc72/73/74 genus-2 + rc75/76/77 genus-3 gates still pass exactly (collapse_g2, duplication_holds, even_null_count, chi18_, transform/sp6_, addition_holds + distinct, hyperelliptic_locus_is_open). (4) Python == C parity EXACT on (1): the native genus-3 Göpel gate decision (holds + has_cross) equals the pure oracle. (5) ratchet-clean (no numpy / math / abs / float on any decision path; AST + JPL audit + tool-schema coverage + rosetta). (6) published-wheel verify (numpy-absent venv outside source tree).
API (carrier methods/classmethods; tools.total unchanged). goepel_syzygy_quad() (the four canonical pairs), goepel_is_syzygous(), goepel_holds(box=3), goepel_lhs(box) / goepel_rhs(box), goepel_is_distinct_from_duplication_addition_and_chi18(box=3). A box-aware diagonal pre-restrict (_diag_restrict, sound: the diagonal A-exponents are non-negative and additive under the pair product) keeps the pure-Python convolution tractable while leaving the safe-region result bit-identical.
The C peer (everything-mirrors, same-rc). srmech_riemann_theta_g3_goepel (+ srmech_riemann_theta_g3_goepel_count) decides the gate exactly — it accumulates the residual LHS − RHS restricted to the safe region and reports holds (residual empty) + has_cross (genuine genus-3 cross-term). Caller-arena (one int64 work[] sized via the count helper), malloc-free, JPL-clean (no goto/abs; the per-term (−1)^{ε·n} sign is the Class-K pin-slot), with the SAME sound diagonal pre-restrict as Python. The Python body is the complete alternative + parity oracle (byte-identical decision). ABI-additive (stays 3).
[0.9.0rc77] - 2026-06-28¶
srmech.amsc.riemann_theta — the genus-3 Sp(6,ℤ) modular TRANSFORMATION + the genus-3 two-argument ADDITION theorem. rc73 gave the genus-2 RiemannTheta a group action (the Sp(4,ℤ) modular transformation) + a genuine two-argument identity (the genus-2 addition theorem). rc77 is the g=2→g=3 PARAMETRIC EXTENSION of that work, on the rc75/76 RiemannThetaG3 carrier: (A) the genus-3 Sp(6,ℤ) modular action on the 6-bit binary characteristic + the κ 8th-root multiplier, and (B) the genus-3 addition theorem (the two-argument case, GENUINELY DISTINCT from the rc75 genus-3 duplication). All new surfaces are CARRIER METHODS / classmethods (the rc72–rc76 carrier precedent), so tools.total is UNCHANGED (340); ABI stays 3 (two additive C symbols). Shipped CO-EQUAL Python + 1:1 native C peer in the SAME rc (the everything-mirrors / never-split discipline).
(A) the genus-3 Sp(6,ℤ) TRANSFORMATION (MPM-verified general-g). The genus-3 modular group Sp(2g,ℤ) = Sp(6,ℤ) (A,B,C,D now 3×3 integer blocks) acts on the binary characteristic m = [ε'; ε] (six bits) by the EXACT affine-linear map ε' ↦ D·ε' − C·ε + diag(C·Dᵀ), ε ↦ −B·ε' + A·ε + diag(A·Bᵀ) (DLMF §21.5.9 is stated for GENERAL genus g with g×g blocks — re-verified at https://dlmf.nist.gov/21.5 that the SAME formula holds at g=3; Igusa, Theta Functions (1972) §V.1). The theta-constant picks up an 8th-root-of-unity multiplier κ = ζ₈^k; the EXACT characteristic-dependent Igusa phase 8·φ_m ∈ ℤ is ON the decision path (k ∈ ℤ/8, exact). The genus-g φ_m is the SAME expression at every g (a sum Σ_{k,l=1}^g; MPM-verified) — the g=2 RiemannTheta._kappa_exp8 formula parametrically extended to 3-vectors / 3×3 blocks. The TRANSCENDENTAL det(C·Ω+D)^{1/2} automorphy factor + the γ-only Maslov κ₀ cocycle stay SYMBOLIC, OFF every decision path (automorphy_factor() returns a string; the rc72/73 lesson). API (carrier methods/classmethods): transform(γ) → (RiemannThetaG3, k), sp6_translation(B) / sp6_gl_twist(A) / sp6_inversion(), sp6_is_symplectic(γ), sp6_compose(g2, g1), automorphy_factor(γ).
(B) the genus-3 ADDITION theorem (genuine; distinct from rc75 duplication). The two-argument genus-3 theta addition theorem θ[a;0]·θ[b;0] = Σ_{r ∈ (ℤ/2)³} θ[(2r+a+b)/2;0](2Ω)·θ[(2r+a−b)/2;0](2Ω) (the g=3 specialization of DLMF §21.6.8 at z₁=z₂=0, lower chars 0 — the sum runs over ν ∈ ℤ^g/2ℤ^g → EIGHT terms at g=3; re-verified at https://dlmf.nist.gov/21.6). It holds EXACTLY as a truncated exact-ℚ multivariate q-series for ALL Ω (a lattice equality in the common EIGHTH-nome base Q₈ = q^{1/8} so θ at Ω AND at 2Ω clear to ONE integer lattice), exercising ALL THREE genus-3 cross-terms. GENUINELY DISTINCT FROM the rc75 DUPLICATION: duplication squares a SINGLE even null (θ[0;0]² = Σ_c θ[c;0](2Ω)², the z=w/single-null case); addition is the BILINEAR product of TWO DIFFERENT nulls θ[a]·θ[b] (a≠b) with DISTINCT characteristics 2r+a+b vs 2r+a−b per summand — duplication alone never produces it; only the a=b collapse recovers duplication. addition_is_distinct_from_duplication() PROVES (no-shell, exact lattice comparison) the genuine addition LHS differs from EVERY θ[c;0]² over the eight even c ∈ {0,1}³. API: addition_holds(box), addition_is_distinct_from_duplication(box), addition_lhs(a,b,box) / addition_rhs(a,b,box).
The exact gates (no-float, no-shell; all PASS, Python and C). (1) Sp(6,ℤ) transform exactness: the characteristic action is bit-exact on the Sp(6,ℤ) generators (translation/GL-twist/inversion J); even⇄even / odd⇄odd parity is PRESERVED across ALL 64 characteristics; the group law composes exactly (transform(g₂·g₁) == transform(g₂)∘transform(g₁) on all 64 chars × all generator pairs, incl. J⁴ acting trivially on chars); κ is the correct 8th-root (k ∈ {0,…,7}, exact). (2) genus-3 addition: addition_holds(box) == True (exact-ℚ, all Ω, genuine a≠b pairs exercising the 3 cross-terms) AND addition_is_distinct_from_duplication == True (proven distinct from the rc75 duplication). (3) No regression: ALL rc72/73/74 genus-2 + rc75/76 genus-3 gates (collapse_g2, duplication_holds, even_null_count, chi18_*, hyperelliptic_locus_is_open, genus-2 collapse/dup/add/Göpel) still pass exactly (98 genus-theta regression tests green). (4) Python == C parity EXACT on (1)+(2): the native Sp(6,ℤ) char+κ (320 checks, 0 mismatch) + the genus-3 eighth-nome lattice at Ω/2Ω (256 checks, 0 mismatch). (5) ratchet-clean (no numpy / math / abs / float on any decision path; AST + JPL audit + tool-schema-coverage + rosetta). (6) published-wheel verify (numpy-absent venv outside source tree).
The C peers (everything-mirrors, same-rc). srmech_riemann_theta_g3_sp6_char mirrors the EXACT integer Sp(6,ℤ) characteristic transform + the κ 8·φ_m exponent (3×3 block helpers: rt3_matvec/rt3_ptq/rt3_pqt/rt3_diag_pqt, the symplectic check, the Igusa phase; gamma is 36 int64 = A,B,C,D 3×3 blocks row-major). srmech_riemann_theta_g3_eighth_lattice (+ srmech_riemann_theta_g3_eighth_count) mirrors the COMMON genus-3 eighth-nome [A1,A2,A3,C12,C13,C23,sign] septuple lattice at Ω / 2Ω that the addition gate convolves. Both caller-arena (malloc-free, JPL-clean, no goto/abs; the per-term (−1)^{ε·n} sign is the Class-K pin-slot); the Python bodies are their complete alternatives + parity oracles (byte-identical). ABI-additive (stays 3).
[0.9.0rc76] - 2026-06-28¶
srmech.amsc.riemann_theta — Igusa's χ₁₈: the genus-3 vanishing-theta-null structure as an EXACT formal q-series. rc75 built the genus-3 RiemannThetaG3 carrier + named the genus-3 hyperelliptic locus as the documented operand-side OPEN. rc76 BUILDS the form behind that OPEN: Igusa's χ₁₈ — the weight-18 degree-3 Siegel cusp form DEFINED AS THE PRODUCT OF ALL 36 EVEN THETA-CONSTANTS (theta-nulls) — as a finite EXACT object in-carrier, extending RiemannThetaG3 with new classmethods (the rc72–rc75 carrier precedent), so tools.total is UNCHANGED (340); ABI stays 3 (additive C symbols). Shipped CO-EQUAL Python + 1:1 native C peer in the SAME rc (the everything-mirrors / never-split discipline).
THE OBJECT (MPM-verified from the PDF). χ₁₈ ∈ S₁₈(Γ₃) is the scalar-valued Siegel cusp form of weight 18 and degree 3, χ₁₈ = ∏ over the 36 even theta-nulls θε (each θ-null a modular form of weight ½ → 36·½ = 18). Its divisor is H₃ + 2D (H₃ the hyperelliptic locus, D the divisor at infinity), so χ₁₈ vanishes EXACTLY on the genus-3 hyperelliptic locus. Re-verified at build from Bernatska–Kopeliovich, "Igusa's map on modular forms vanishing on the hyperelliptic locus", arXiv:2306.14889 (p.1, the genus-3 exact sequence 0 → χ₁₈𝔄(Γ₃) → 𝔄(Γ₃) →^ρ 𝒮(2,8), "χ₁₈ the cusp form of weight 18, defined as the product of all even theta constants" — PDF re-fetched to docs/srmech/hoodoos/bernatska_kopeliovich_2023_igusa_map_hyperelliptic_2306.14889.pdf as load-bearing) + van der Geer, Siegel Modular Forms of Degree Two and Three and Invariant Theory (the degree-3 χ₁₈ as ∏₃₆ even nulls).
THE EXACT, NO-SHELL CONTENT. The 36 even theta-nulls are each an EXACT formal q-series in the rc75 genus-3 sextuple nome lattice. χ₁₈ = their product is a well-defined NONZERO formal q-series with a COMPUTABLE leading q-order. rc76 computes the EXACT leading-order homogeneous part (the cusp-vanishing structure) exactly (exact-integer coefficients, numpy-free): each even null's leading diagonal slice (its minimal-Σ Aᵢ monomials, min Σ (2nᵢ+ε'ᵢ)² = wt(ε')) is convolved across all 36 nulls. The leading diagonal q-order = 48 quarter-nome units = 12 in the diagonal nome qᵢ — derived BOTH from the lattice product AND from the independent combinatorial oracle Σ wt(ε') over the 36 evens (8 nulls of wt 0 + 12 of wt 1 + 12 of wt 2 + 4 of wt 3 = 0·8 + 1·12 + 2·12 + 3·4 = 48). The product's leading-order COEFFICIENT is NONZERO (109 leading-part monomials; verified — no cancellation drops the order), so χ₁₈ ≢ 0 (a genuine weight-18 cusp form). API (carrier classmethods): chi18_even_null_factors() (the 36 even-null factors), chi18_leading_part(box=2) (the exact leading-part lattice, native-dispatched), chi18_leading_order_quarter() → 48 / chi18_leading_order_nome() → 12, chi18_is_nonzero() → True, chi18_leading_part_is_at_order_48() → True, chi18_factor_count_is_36_even() → True.
THE HONEST OPEN, PRESERVED + now NAMED. The NUMERICAL decision "is THIS Ω hyperelliptic / does χ₁₈(Ω) = 0" is a POINT-EVALUATION of χ₁₈ at a transcendental Ω ∈ H₃ (only knowable to N digits = float on the decision path) — NOT a finite exact carrier op → the operand-side OPEN. rc76 upgrades hyperelliptic_locus_is_open() to reference χ₁₈ explicitly ("the form whose transcendental vanishing decides it", divisor H₃ + 2D) while building NO numerical hyperelliptic / χ₁₈-vanishing decision (the rc72/74/75 lesson — is_hyperelliptic / chi18_vanishes / chi18_evaluate do NOT exist). χ₁₈ is provided as the exact FORM (the construction = the 36-even-null product) + the numerical vanishing-decision stays OPEN.
The exact gates (no-float, no-shell; all PASS, Python and C). (1) χ₁₈ exact construction: chi18_leading_part is the exact formal-q-series leading part = product of the 36 even nulls; NONZERO, leading q-order 48 quarter (= 12 in qᵢ), product of EXACTLY 36 even nulls (each a genuine even null; the singular empty-set null among them). (2) Combinatorics: the 36-even ↔ wt(ε')-partition map (8/12/12/4) consistent with rc75 (even_null_count() → (36, 28), singular = empty-set char). (3) Honest OPEN preserved: hyperelliptic_locus_is_open() references χ₁₈ + returns the OPEN string; no numerical decision built. (4) No regression: ALL rc72/73/74 genus-2 + rc75 genus-3 gates (collapse_g2, duplication_holds, even_null_count, addition/Göpel/Rosenhain, …) still pass exactly. (5) Python == C parity EXACT on the χ₁₈ leading part. (6) published-wheel verify (numpy-absent venv outside source tree).
The C peer (everything-mirrors, same-rc). srmech_riemann_theta_g3_chi18 (+ srmech_riemann_theta_g3_chi18_count) mirrors the genuinely-new exact computation — the 36-even-null leading-part convolution, each null's leading diagonal slice generated DIRECTLY (nᵢ ∈ {0} if ε'ᵢ=0 else {0,−1}), ping-ponged over a caller arena into the canonical [A1,A2,A3,C12,C13,C23,coeff] septuple lattice (coefficients int64-exact, max |coeff| = 2³⁴, no bignum; the per-term (−1)^{ε·n} sign is the Class-K pin-slot, never abs). Caller-arena (malloc-free, JPL-clean, no goto/abs); the Python body is its complete alternative + parity oracle (byte-identical, 109 monomials). ABI-additive (stays 3).
[0.9.0rc75] - 2026-06-27¶
srmech.amsc.riemann_theta — the NEXT GENUS RUNG: the genus-3 RiemannThetaG3 carrier. rc72–rc74 built the genus-2 Riemann theta-CONSTANT (the first rung of the GENUS axis) + its modular action + the Thomae/Rosenhain capstone. rc75 climbs the GENUS axis one rung: a numpy-free EXACT genus-3 Riemann theta-constant θ[ε'; ε](0 | Ω) over a 3×3 SYMMETRIC Ω ∈ H₃ (the Siegel upper half space, dim g(g+1)/2 = 6) — the genus-3 analog of the rc72 genus-2 first rung, extending the existing riemann_theta module with a NEW class RiemannThetaG3 (a genus-3-specific extension; the rc72/73/74 genus-2 surfaces are UNTOUCHED — zero regression). All new surfaces are CARRIER METHODS / classmethods (the rc72 duplication_holds precedent), so tools.total is UNCHANGED (340); ABI stays 3 (one additive C symbol). Shipped CO-EQUAL Python + 1:1 native C peer in the SAME rc (the everything-mirrors / never-split discipline).
THE OBJECT. Binary characteristic [ε'; ε], ε', ε ∈ {0,1}³ → 64 total characteristics = 36 EVEN + 28 ODD (Grushevsky, The Schottky Problem, arXiv:1009.0369: "there are 2^{g-1}(2^g+1) even theta constants" → g=3 gives 4·9 = 36 even, 4·7 = 28 odd, MPM-verified from the PDF; the empty-set even null [0,0,0;0,0,0] is the distinguished singular one). A characteristic is even iff ε'·ε ≡ 0 (mod 2).
THE EXACT NOME-LATTICE (no float on the decision path; THE HARDEST PART = THREE cross-terms). The carrier represents the theta-CONSTANT as an EXACT INTEGER exponent lattice over 3 diagonal nomes qᵢ = e^{iπΩᵢᵢ} + 3 CROSS-TERMS q₁₂, q₁₃, q₂₃ = e^{2iπΩᵢⱼ} (vs genus-2's ONE cross-term — this is the genuinely-new genus-3 content). With mᵢ = nᵢ + ½ε'ᵢ, cleared to the quarter-nome base Qᵢ = qᵢ^{1/4} / Q_ij = q_ij^{1/4}, a lattice point is a 6-index SEXTUPLE (A₁,A₂,A₃,C₁₂,C₁₃,C₂₃) with EXACT INTEGER exponents Aᵢ = (2nᵢ+ε'ᵢ)² and C_ij = (2nᵢ+ε'ᵢ)(2nⱼ+ε'ⱼ) — each cross-term a PRODUCT of two half-integers → a denominator-4 integer-lattice clearing, now across THREE coupled pairs. Box truncation |nᵢ| ≤ box → (2·box+1)³ monomial terms; each coefficient is an exact integer (a sum of ±1 lattice counts); the sign (−1)^{ε·n} is the Class-K pin-slot (an explicit ±1 branch, never an ALU abs()). FOUNDATION-FIRST: the 3-index exponent-lattice CLEARING (all 64 characteristics, the 3 cross-terms, denominator-4 handling) was built + verified as its own unit FIRST, gated on the collapse, before the formal relation.
The exact gates (no-float, no-shell; all PASS, Python and C). (1) Collapse g3→g2 (THE foundation gate): θ[0,0,0;0,0,0].collapse_g2() collapses EXACTLY to the rc72 genus-2 trivial RiemannTheta (set n₃=0, q₃=q₁₃=q₂₃=1, ε'₃=ε₃=0) — bit-exact vs the existing rung AND DERIVED from the lattice n₃=0 slice (collapse_g2_lattice_matches(box) == True, box-stable), not a hardcoded return; the all-trivial chain → genus-1 θ₃ (collapse_g1_q_series → [1,2,0,0,2,…]). A characteristic with a NON-trivial 3rd component HONESTLY REFUSES to collapse (raises — the rc72 collapse pattern, an honest boundary, not a fabricated reduction). (2) Formal genus-3 theta-null identity: the genus-3 Gauss / DUPLICATION identity θ[0;0](0|Ω)² = Σ_{c ∈ (½ℤ³/ℤ³)} θ[c;0](0|2Ω)² (8 summands) holds EXACTLY as a truncated exact-integer multivariate q-series for ALL Ω — the a=b=0, z=w=0, g=3 specialization of the generalized Riemann theta identity (Chai, "Riemann's theta formula" (2014), Thm 1.2 example (b), the 2^{-g} sum over c ∈ 2^{-1}ℤ^g/ℤ^g, MPM-verified from the PDF; classically Mumford, Tata Lectures on Theta I, the genus-g duplication). The eight θ[c;0] include the (½,½,½) + mixed characteristics with C₁₃ ≠ 0/C₂₃ ≠ 0, so the identity genuinely exercises ALL THREE cross-terms — proving genuine genus-3 theta-constants, not the genus-2/genus-1 slice (duplication_holds(2/3/4) == True). (3) No regression: ALL rc72 (collapse_g1 == θ₃, duplication_holds), rc73 (addition_holds, addition_is_distinct_from_duplication, sp4/transform), rc74 (goepel_holds, goepel_is_distinct_*, rosenhain_lambda_map_is_well_formed) gates still PASS exactly. (4) Python == C parity EXACT on the genus-3 lattice (all 64 characteristics, several boxes) + the gates through the native peer. (5) ratchet-clean (no numpy / math / abs / float on any decision path; AST + JPL audit + tool-schema-coverage + rosetta). (6) published-wheel verify (numpy-absent venv outside source tree).
The genus-3 NEW structure — the documented honest boundary (full numerical op = rc76, NOT this rc). Unlike genus 2 (where EVERY curve is hyperelliptic), the GENERIC genus-3 curve is NON-hyperelliptic (a smooth plane quartic); the HYPERELLIPTIC locus inside A₃ is cut out by a VANISHING even theta-null — an Igusa-type modular form vanishing on the hyperelliptic locus (Poor [Poo96]; Grushevsky arXiv:1009.0369 Thm 3.9/5.2, MPM-verified). DECIDING "is this Ω hyperelliptic" is a POINT-EVALUATION of that null at a transcendental Ω ∈ H₃ → NOT a finite exact carrier op → the operand-side OPEN: hyperelliptic_locus_is_open() returns the honest OPEN string (the rc74 rosenhain_branch_point_recovery_is_open pattern); the carrier provides the FORMAL exact content (even_null_count() → (36, 28), singular_even_null(), duplication_holds()) but builds NO numerical hyperelliptic decision. Schottky: genus 3 is STILL clean (dim M₃ = 3g−3 = 6 = g(g+1)/2 = dim A₃; J₃ = A₃^ind — every indecomposable genus-3 ppav is a Jacobian, Grushevsky: "the dimensions coincide for g ≤ 3 … iff g ≤ 3"); the Schottky frontier OPEN stays at g ≥ 4.
The C peer (everything-mirrors, same-rc). srmech_riemann_theta_g3_lattice (+ srmech_riemann_theta_g3_count) mirrors the genuinely-new exact-integer kernel — the genus-3 (A₁,A₂,A₃,C₁₂,C₁₃,C₂₃) exponent lattice with the THREE cross-terms' denominator-4 clearing + the per-term Class-K sign, emitted as flat caller-owned int64 [A1,A2,A3,C12,C13,C23,sign] septuples (theta-CONSTANT coefficients are small ±1 lattice counts → int64-exact, no bignum). Caller-arena (malloc-free, JPL-clean, no goto/abs); the Python body is its complete alternative + parity oracle. ABI-additive (stays 3).
[0.9.0rc74] - 2026-06-27¶
srmech.amsc.riemann_theta — the GENUS-AXIS CAPSTONE: the Thomae / Rosenhain bridge. rc72 made the genus-2 Riemann theta-CONSTANT a finite exact object; rc73 gave it a modular GROUP ACTION + a genuine addition theorem; rc74 closes the genus-2 axis with the THOMAE / ROSENHAIN bridge — the geometric link from the even theta-NULLS to the hyperelliptic curve y² = x(x−1)(x−λ₁)(x−λ₂)(x−λ₃) — in its exact, no-float, no-shell content: a GENUINE NEW even-null syzygy + the SYMBOLIC Rosenhain λ-map + a documented operand-OPEN. All new surfaces are CARRIER METHODS / classmethods on RiemannTheta (the rc72 duplication_holds / rc73 addition_holds precedent), so tools.total is UNCHANGED (340); ABI stays 3 (one additive C symbol). Shipped CO-EQUAL Python + 1:1 native C peer in the SAME rc (the everything-mirrors / never-split discipline).
THE rc72 REVIEW LESSON, HELD. Thomae's formula relates theta-nulls to branch points, but evaluating it needs the theta-constants at the curve's TRANSCENDENTAL period matrix Ω (only checkable to N digits = float on the decision path). rc74 therefore ships NO numerical Thomae gate. Its exact content is the FORMAL algebraic relations (Rosenhain/Göpel theta-null syzygies, exact-ℚ truncated q-series, ALL Ω) + the SYMBOLIC λ-map (theta-null ratios, NOT numbers); the numerical branch-point recovery is the honest documented OPEN.
(A) The GENUINE NEW exact relation — the Frobenius / Göpel quadratic theta-null syzygy. θ²[a]·θ²[b] = θ²[c]·θ²[d] − θ²[e]·θ²[f] among the genus-2 even theta-NULLS, holding EXACTLY for ALL Ω as a truncated exact-integer multivariate q-series — the genus-2 specialization of the quartic Riemann theta relation (DLMF §21.6, eq. 21.6.6/21.6.7; Mumford, Tata Lectures on Theta II, the genus-2 Göpel/Frobenius relations; Igusa, Theta Functions (1972), §IV). The canonical representative pairs a=[0,0;0,0] b=[1,1;1,1] | c=[0,0;1,1] d=[1,1;0,0] | e=[0,1;1,0] f=[1,0;0,1] — six DISTINCT even nulls forming a GÖPEL SYSTEM (the three pairs all share ONE GF(2) characteristic sum [1,1;1,1], the syzygy fingerprint). GENUINELY DISTINCT from rc72 duplication AND rc73 addition, proved (no shell): both duplication and addition relate the nulls at Ω to nulls at 2Ω (their right sides live at 2Ω), whereas the Göpel syzygy is purely at the SAME Ω (no Ω-doubling); and its LHS is a DEGREE-4 product of four nulls where BOTH the duplication LHS θ[0;0]² and every addition LHS θ[a]θ[b] are DEGREE-2 — so it cannot equal either (goepel_is_distinct_from_duplication_and_addition() verifies the lattice inequality exactly). The inner region is proven box-STABLE (identical across box = 4, 5, 6 at a fixed bound), the no-truncation-artifact guarantee. API: goepel_holds(box=5) → bool, goepel_syzygy_triple(), goepel_is_syzygous(), goepel_lhs / goepel_rhs, goepel_is_distinct_from_duplication_and_addition(box=5).
(B) The SYMBOLIC Rosenhain λ-map (theta-null ratios, NOT numbers). The 3 Rosenhain moduli (λ₁, λ₂, λ₃) of y² = x(x−1)(x−λ₁)(x−λ₂)(x−λ₃) as FORMAL theta-null RATIOS — each a cross-ratio of branch points expressed as ± θ²[ε(k,S)]·θ²[ε(k,T)] / (θ²[ε(l,S)]·θ²[ε(l,T)]) (Eilers, Rosenhain–Thomae Formulae for Higher Genera Hyperelliptic Curves, arXiv:1707.08855, Cor 2.4 eq. 2.18; Rosenhain's modular representation, eqs 1.4–1.7), via the EXACT Eilers genus-2 η-map [ε(I)] = Σ_{k∈I} [𝔄_k] − [K∞] (mod 2) (eq 4.4; [𝔄_k] eq 4.2, [K∞] eq 4.3). Each λ is returned SYMBOLICALLY — a dict of numerator/denominator EVEN-null characteristics + the branch-index data + the symbolic cross-ratio string — a FORMAL exact-q-series-ratio DEFINITION, NEVER a number. The η-map is internally consistent (6 single indices → the 6 ODD chars; 10 finite pairs → the 10 EVEN nulls). API: rosenhain_lambda_map(), rosenhain_lambda_map_is_well_formed(), branch_set_characteristic(indices) (the η-map), riemann_constant().
(C) The documented operand-side OPEN. rosenhain_branch_point_recovery_is_open() returns the honest OPEN STATEMENT (a string, never a number): recovering numerical branch points / a numerical λ from the theta-nulls needs the TRANSCENDENTAL period map (theta evaluated at the curve's Ω ∈ H₂, only knowable to N digits = float on the decision path) — NOT a finite exact carrier operation. The carrier provides the formal exact content and REFUSES to fabricate a numerical λ (the rc72 review lesson).
The C peer (everything-mirrors, same-rc). srmech_riemann_theta_eta_char mirrors the genuinely-new exact-integer kernel — the Eilers η-map (branch-point index set → characteristic), pure GF(2) / mod-2 linear algebra over a caller-owned int[4] (malloc-free, JPL-clean; subtraction == addition in (ℤ/2)⁴ so NO sign branch / NO abs() is even needed). The Göpel relation gate itself convolves the existing srmech_riemann_theta_lattice outputs (caller bookkeeping, already C-backed), so the η-map is rc74's new C kernel; the Python body is its complete alternative + parity oracle. ABI-additive (stays 3).
The exact gates (no-float, no-shell; all PASS, Python and C). (1) Göpel syzygy: goepel_holds(4/5/6) == True as an exact truncated-ℚ q-series identity for ALL Ω, genuinely exercising the cross-term q₁₂, with the inner region box-STABLE; AND goepel_is_distinct_from_duplication_and_addition == True (degree-4 vs degree-2; Ω-only vs Ω-vs-2Ω). (2) λ-map: rosenhain_lambda_map_is_well_formed == True — the three λᵢ use the correct η-map characteristics (no fabrication), every ratio entry is an EVEN null, three distinct moduli; the result is symbolic (bit-tuple characteristics + strings), never numeric. (3) No regression: rc72 (collapse_g1 == θ₃ bit-exact; duplication_holds(8)) + rc73 (addition_holds, addition_is_distinct_from_duplication, transform/sp4 parity + κ) all still pass exactly. (4) Python == C parity EXACT on the η-map (all singletons, all pairs, the empty set). (5) ratchet-clean (no numpy / math / abs / float on any decision path). (6) the OPEN is a string, never a number.
[0.9.0rc73] - 2026-06-27¶
srmech.amsc.riemann_theta — the SECOND GENUS RUNG: the Sp(4,ℤ) TRANSFORMATION + the genus-2 ADDITION relation. rc72 made the genus-2 Riemann theta-CONSTANT a finite exact object; rc73 gives it a GROUP ACTION and a GENUINE two-argument identity — the two structures that turn an isolated object into a carrier with a modular symmetry and a within-carrier addition theorem. Both shipped CO-EQUAL in Python AND 1:1 native C peers in the SAME rc (the everything-mirrors / never-split discipline): srmech_riemann_theta_sp4_char (the exact characteristic transform + the κ exponent) and srmech_riemann_theta_eighth_lattice (the common eighth-nome lattice the addition gate convolves). All new surfaces are CARRIER METHODS / classmethods on RiemannTheta (the rc72 duplication_holds precedent), so tools.total is UNCHANGED (340); ABI stays 3 (additive symbols).
(A) The Sp(4,ℤ) TRANSFORMATION (the modular action on characteristics). The genus-2 modular group Sp(2g,ℤ) = Sp(4,ℤ) acts on the binary characteristic m = [ε'; ε] by the EXACT affine-linear map (DLMF §21.5, eq. 21.5.9; Igusa, Theta Functions (1972), §V.1): ε' ↦ D·ε' − C·ε + diag(C·Dᵀ), ε ↦ −B·ε' + A·ε + diag(A·Bᵀ) (reduced mod 2 for the bit). The action factors through Sp(4,ℤ₂) and PRESERVES the even/odd parity (even ⇄ even, odd ⇄ odd — the level-2 theory; Bruinier et al., The 1-2-3 of Modular Forms). The theta-constant gains an 8th-root-of-unity multiplier κ(γ) = ζ₈^k carried as the EXACT integer exponent k ∈ ℤ/8 from the Igusa phase φ_m(γ) = −½·ε'ᵀ(B·Dᵀ)ε' + εᵀ(AᵀC)ε − 2·ε'ᵀ(BᵀC)ε − diag(A·Bᵀ)ᵀ(D·ε' − C·ε) (a rational with denominator dividing 8, so 8·φ_m is an EXACT integer — k = 8·φ_m mod 8, no float on the decision path). The TRANSCENDENTAL automorphy factor det(C·Ω+D)^{1/2} is NEVER numerically evaluated — automorphy_factor(γ) returns it SYMBOLICALLY (a string), off every gate (the rc72 review lesson). API: transform(γ) → (RiemannTheta, k); the standard generator constructors sp4_translation(B) ([[I,B],[0,I]], B symmetric — DLMF 21.5.6), sp4_gl_twist(A) ([[A,0],[0,(Aᵀ)⁻¹]], A ∈ GL(2,ℤ) — DLMF 21.5.5), sp4_inversion() ([[0,−I],[I,0]] — DLMF 21.5.8); sp4_is_symplectic(γ) (the exact integer γ·J·γᵀ = J check) and sp4_compose(g₂,g₁) (the block-matrix group law).
(B) The genus-2 ADDITION relation (genuine; distinct from rc72's duplication). The GENUINE two-argument genus-2 theta addition theorem (DLMF §21.6, eq. 21.6.8, the z₁ = z₂ = 0, two-independent-characteristic specialization) is the BILINEAR product of TWO theta-nulls θ[a;0](0|Ω) · θ[b;0](0|Ω) = Σ_{r∈(ℤ/2)²} θ[(2r+a+b)/2; 0](0|2Ω) · θ[(2r+a−b)/2; 0](0|2Ω), derived CONSTRUCTIVELY by the sum/difference re-indexing M = m+m', M' = m−m' of the double lattice sum. It holds for ALL Ω (a formal identity) → exactly checkable as a truncated exact-integer multivariate q-series (no transcendental evaluation, no float, no tolerance). Both theta at Ω and theta at 2Ω clear to ONE common integer lattice in the EIGHTH-nome base Q₈ = q^{1/8} (θ(Ω): A = 2(2n+s)² …; θ(2Ω): A = (4n+s)² …), so the identity is a LATTICE EQUALITY on the safe inner region. GENUINELY DISTINCT from duplication, proved (no shell): duplication squares a SINGLE even theta-null (θ[0;0]² = Σ_c θ[c;0](2Ω)²); the addition relation is a product of TWO DIFFERENT nulls θ[a]·θ[b] (a ≠ b), with the right side carrying DISTINCT characteristics 2r+a+b vs 2r+a−b per summand — content duplication ALONE never produces (it never holds a product of two distinct nulls). addition_is_distinct_from_duplication() proves the genuine a ≠ b LHS differs from EVERY θ[c;0]² lattice; the a = b collapse recovers rc72's duplication. API: addition_holds(box=8) → bool (the gate; verifies genuine a ≠ b pairs, not just the a = b collapse), addition_lhs / addition_rhs, addition_is_distinct_from_duplication(box=8).
The exact gates (no-float, no-shell; all PASS, Python and C). (1) Transformation exactness: the characteristic action is bit-exact on the S/T/U generators; even ⇄ odd parity transforms correctly on all 16 characteristics; the group law composes exactly (transform(g₂·g₁) == transform(g₂)∘transform(g₁)); κ(γ) is the correct 8th root (k ∈ {0,…,7}, exact). (2) Addition: addition_holds(4/6/8) == True as an exact truncated-ℚ q-series identity, for ALL Ω, AND it is the GENUINE addition theorem (addition_is_distinct_from_duplication == True; the a ≠ b LHS ≠ any θ[c]²), genuinely exercising the cross-term q₁₂. (3) No regression: rc72's gates still pass exactly — collapse_g1(trivial) == θ₃ bit-exact; duplication_holds(8) == True. (4) Python == C parity EXACT on the transformation (characteristic + κ, all 16 chars × all generators) and the eighth-nome lattice (at Ω and 2Ω, several boxes × characteristics). (5) ratchet-clean (no numpy / math / abs / float on any decision path in the carrier source — the (−1)^{ε·n} sign is the Class-K pin-slot, the det(Cτ+D)^{1/2} factor is symbolic).
Honesty boundaries (kept). The transcendental automorphy factor det(C·Ω+D)^{1/2} and theta values at transcendental Ω are NEVER on a decision path — symbolic only. A non-symplectic γ, an asymmetric translation block, or a non-unimodular GL-twist block RAISES honestly (the rc72 "refuse to fabricate" pattern), never fakes a reduction. The Schottky frontier (g ≥ 4) stays the documented OPEN; genus 2 is clean.
References (MPM-verified at build — DLMF is OA, equation numbers confirmed against the live sections; the rc72-saved arXiv PDFs reused). DLMF, Digital Library of Mathematical Functions, §21.5 Modular Transformations (eq. 21.5.9 the theta-with-characteristics transformation law [Dα−Cβ+½diag(CDᵀ); −Bα+Aβ+½diag(ABᵀ)] + the automorphy factor det(CΩ+D); eqs. 21.5.5/21.5.6/21.5.8 the generators; eq. 21.5.2 the symplectic condition γJγᵀ=J) and §21.6 Products (eq. 21.6.8 the two-argument addition theorem θ[α/γ](z₁)θ[β/δ](z₂) = Σ_ν θ[½(α+β+ν)/(γ+δ)](z₁+z₂|2Ω) θ[½(α−β+ν)/(γ−δ)](z₁−z₂|2Ω); eq. 21.6.6 the four-theta Riemann relation). Igusa, Theta Functions, Grundlehren 194, Springer (1972), §V.1 (the explicit phase φ_m + the 8th-root multiplier κ). Bruinier–van der Geer–Harder–Zagier, The 1-2-3 of Modular Forms (2008) (the level-2 / Sp(2g,ℤ₂) action on characteristics, parity transitivity). The rc72 theta-constant definition + the genus-2 carrier: Grushevsky (arXiv:1009.0369), Eilers (arXiv:1707.08855).
Tests. tests/test_riemann_theta_rc73.py: (A) the generators are symplectic + reject malformed blocks; the characteristic action preserves parity on all 16 chars × all generators; bit-exact concrete maps; the group law composes exactly; κ is the correct 8th-root exponent (incl. the translation k ∈ {0,4} values); the automorphy factor is symbolic not evaluated. (B) addition_holds(4/6/8); the no-shell distinctness proof; the a=b duplication collapse; the cross-term exercise. (C) rc72 collapse + duplication no-regression. (D) Python==C parity on the transform + the eighth-nome lattice + the gates through the native path. (E) the no-numpy/no-math/no-abs/no-float source guard. A pure carrier extension → no ToolEntry, no Rosetta row, tools.total stays 340.
Rosetta: unchanged — the rc73 surfaces are CARRIER METHODS on RiemannTheta (no public op), so they add no Rosetta classification row (the rc72 / ThetaSum / QMat precedent); the two DEBT-bucket ceilings are unchanged.
[0.9.0rc72] - 2026-06-27¶
srmech.amsc.riemann_theta — RiemannTheta, the FIRST RUNG of the new GENUS axis. The operand carrier ladder's elliptic / theta carriers (EllRatio / ThetaSum / UnaryTheta) all live on a SINGLE genus-1 torus (one period τ, the upper-half-plane H₁) — a real ceiling: a genus-2 abelian variety carries a Riemann theta of TWO complex variables over a 2×2 Riemann matrix Ω ∈ H₂ (the Siegel upper half space), and the cross-period Ω₁₂ coupling has NO genus-1 representative. RiemannTheta augments the ladder with a GENUS axis and makes the genus-2 Riemann theta-CONSTANT a finite exact object in-carrier. Shipped CO-EQUAL in Python AND a 1:1 native C peer srmech_riemann_theta in the SAME rc (the everything-mirrors / never-split discipline). A pure CARRIER (like ThetaSum / EllRatio / QMat): no public ToolEntry op, so tools.total is UNCHANGED (340); ABI stays 3 (additive symbols).
The object + the EXACT NOME-LATTICE clearing (the hardest part — the cross-term). The genus-2 theta-constant with binary characteristic [ε'; ε] is θ[ε';ε](0|Ω) = Σ_{n∈ℤ²} (−1)^{ε·n} q₁^{m₁²} q₂^{m₂²} q₁₂^{m₁m₂}, mᵢ = nᵢ + ½ε'ᵢ, nome alphabet q₁=e^{iπΩ₁₁}, q₂=e^{iπΩ₂₂}, q₁₂=e^{2iπΩ₁₂} (Grushevsky, The Schottky Problem, arXiv:1009.0369, eq. (1); Eilers, Rosenhain–Thomae Formulae for Higher Genera Hyperelliptic Curves, arXiv:1707.08855, eq. (1.2); genus 2 has 16 characteristics = 10 even + 6 odd, Eilers p. 2, even iff ε'·ε ≡ 0 mod 2). Cleared to the QUARTER-nome base (Q₁,Q₂,Q₁₂) = (q₁,q₂,q₁₂)^{1/4}, a lattice term is Q₁^A Q₂^B Q₁₂^C · (−1)^{ε·n} with EXACT INTEGER exponents A = (2n₁+ε'₁)² = 4n₁²+4n₁ε'₁+ε'₁², B = (2n₂+ε'₂)², and the genus-2 CROSS-TERM C = (2n₁+ε'₁)(2n₂+ε'₂) = 4n₁n₂+2n₁ε'₂+2n₂ε'₁+ε'₁ε'₂. THE CROSS-TERM C is the genuinely-new, hardest part: m₁m₂ is a PRODUCT of two half-integers, so it carries a DENOMINATOR 4 in the cleared integer lattice (the genus-1 carriers never saw this n₁n₂ coupling). The finite generating rule is the lattice-box truncation |nᵢ| ≤ box; the lower characteristic ε contributes a per-term SIGN (−1)^{ε·n} — the Class-K pin-slot via an explicit ±1 parity branch, never an ALU abs() (the common phase i^{ε·ε'} factors out). Each lattice coefficient is an exact INTEGER (a sum of ±1 lattice counts), so the carrier is exact-integer all the way — no float, no math, no numpy, no float on any decision path.
The two exact gates (no-float, no-shell; all PASS, Python and C). (1) COLLAPSE (primary foundation gate): θ[0,0;0,0].collapse_g1() sets Ω₁₂ = Ω₂₂ = 0 (⇒ q₂ = q₁₂ = 1) and n₂ = 0, leaving Σ_{n₁} q₁^{n₁²} = the genus-1 Jacobi theta θ₃ — returned as the rc70 UnaryTheta unary_theta('trivial', 0, 1, 0, 1, support='all'), BIT-EXACT vs the existing rung (q_series(20) == [1,2,0,0,2,…]). Only the trivial even characteristic collapses to plain θ₃; any other characteristic's genus-1 slice is a shifted / signed theta and is REJECTED (an honest boundary, not a fabricated reduction). (2) FORMAL genus-2 theta-null identity (secondary): the genus-g Gauss / duplication identity θ[0;0](0|Ω)² = Σ_{c∈(½ℤ²/ℤ²)} θ[c;0](0|2Ω)² holds EXACTLY as a truncated exact-integer multivariate q-series, for ALL Ω — NO transcendental evaluation (the z=w=0, a=b=0 specialization of the generalized Riemann theta identity: Chai, Riemann's theta formula (2014), Thm 1.2 example (b); classically Mumford, Tata Lectures on Theta I (1983), the genus-g duplication). The four θ[c;0] (c ∈ {0,½}²) are all even theta-nulls, and the sum genuinely exercises the cross-term q₁₂ (the (½,½) and mixed characteristics have C ≠ 0) — so it PROVES the carrier computes genuine genus-2 theta-constants, not just the genus-1 slice. The gate compares the two sides ONLY on the SAFE INNER REGION the box provably resolves (a box-box theta omits only terms with a quarter-nome exponent ≥ 4(box+1)², so monomials with A, B, |C| ≤ 4·box² are fully accumulated — a wrong box fails loudly, never silently). (3) Python==C parity EXACT on the lattice for all 16 characteristics × several boxes. (4) ratchet-clean (no numpy / math / abs / float in the carrier source).
The named operand-side OPEN (documented, honest — NOT faked). The carrier is REPRESENTABLE (a finite exact decision: the canonical nome-monomial form + the finite Riemann relations, box pinned by the polarization level). The genus-axis OPEN is the SCHOTTKY PROBLEM — which Ω ∈ H_g are Jacobians of curves (dim M_g = 3g−3 vs dim A_g = g(g+1)/2; they coincide for g ≤ 3, so the Jacobian locus is everything there; g = 4 is the first non-trivial case, solved by Schottky; g ≥ 5 is genuinely open — Grushevsky, arXiv:1009.0369, p. 5 + Open Problem 1, p. 6). Genus 2 is CLEAN (dim M_2 = 3 = dim A_2, no Schottky obstruction), which is exactly why it is the first representable rung. This is the dual of an operator-side honest None (the F929 operand program — enlarge the carrier to turn a former irrepresentable into a finite exact reduction).
Hyperelliptic / Thomae = MOTIVATION + the rc74 target (NOT this rc's exact gate). A genus-2 Jacobian carries this Riemann theta, and Thomae's formula (Eilers eq. (2.30) / Cor. 2.4) is the geometric bridge from the even theta-nulls to the branch points of y² = x(x−1)(x−a₁)(x−a₂)(x−a₃). That is the MOTIVATION and the rc74 target. It is deliberately NOT a gate here: verifying Thomae requires evaluating theta-constants at a curve's TRANSCENDENTAL period matrix Ω (only checkable to N digits = float on the decision path = a thin shell), which the discipline FORBIDS. rc74 handles it via the FORMAL Rosenhain / Göpel algebraic relations among the theta-nulls + a DOCUMENTED (not numerically-evaluated) transcendental λ-map.
The 1:1 C peer (same-rc). srmech_riemann_theta mirrors the genuinely-new computation — the EXACT INTEGER (A, B, C) exponent lattice with the cross-term denominator-4 clearing C = (2n₁+ε'₁)(2n₂+ε'₂) + the per-term Class-K sign (−1)^{ε·n} — over a caller-owned int64 array. The genus-2 theta-CONSTANT coefficients are small ±1 lattice counts (int64-exact, NO bignum needed), emitted as [A,B,C,sign] QUADRUPLES (one per lattice point, row-major), which the Python marshaller accumulates into the byte-identical canonical {(A,B,C): coeff} lattice; the Python .lattice() DISPATCHES to it via has_native_riemann_theta() and falls cleanly to the COMPLETE pure-Python body (the C peer's parity oracle) otherwise. Caller-owned out[] (no malloc, JPL Rule 3), JPL Power-of-Ten clean (Release/-DNDEBUG pedantic verified — the assert-only path is #define-stripped, no -Wunused); the sign is the Class-K pin-slot, never abs(); no math / numpy / libm.
References (MPM-verified at build — the actual PDFs extracted, equation/page numbers + authors/titles/arXiv-IDs confirmed; saved to docs/srmech/hoodoos/). Keno Eilers, Rosenhain–Thomae Formulae for Higher Genera Hyperelliptic Curves (arXiv:1707.08855): eq. (1.2) (the genus-2 theta-constant with the binary characteristic [ε'₁ε'₂; ε₁ε₂]), p. 2 (10 even + 6 odd characteristics), p. 9 (the genus-1 collapse example), §2 / Cor. 2.4 (Thomae, the rc74 motivation). Samuel Grushevsky, The Schottky Problem (arXiv:1009.0369): eq. (1) (the Riemann theta on H_g), Def. 2.3 (Siegel space), p. 5 (the dimension count dim M_g = 3g−3 vs dim A_g = g(g+1)/2, coincide for g ≤ 3), Open Problem 1 p. 6 (g = 5 open). Ching-Li Chai, Riemann's theta formula (2014): Thm 1.2 example (b) (the duplication / quadratic theta relation), referencing Mumford, Tata Lectures on Theta I (1983).
Tests. tests/test_riemann_theta_rc72.py: the genus axis (genus == 2; 10 even + 6 odd); the exponent-lattice clearing (diagonal (2nᵢ+ε'ᵢ)² + THE cross-term C = (2n₁+ε'₁)(2n₂+ε'₂) denominator-4 handling, all 16 characteristics exact integers); THE COLLAPSE GATE (collapse_g1() == rc70 θ₃ bit-exact; only the trivial characteristic, else honest reject); THE FORMAL DUPLICATION GATE (the identity holds exactly on the safe region AND genuinely touches the cross-term); Python==C parity on .lattice for all 16 characteristics × 5 boxes + the gates through the native path (guarded by the native-availability skip); input validation; the no-numpy/no-math/no-abs/no-float source guard. A pure carrier → no ToolEntry, no Rosetta row, tools.total stays 340.
Rosetta: unchanged — RiemannTheta is a CARRIER class (no public op), so it adds no Rosetta classification row (like ThetaSum / QMat / Poly); the two DEBT-bucket ceilings are unchanged.
[0.9.0rc71] - 2026-06-27¶
srmech.amsc.harmonic_maass — HarmonicMaass, the PAIR carrier that makes a harmonic (weak) Maass form a FINITE EXACT object (research item #9 CLOSED). rc70 made the mock-theta SHADOW representable in-carrier (the weight-3/2 UnaryTheta g₃); rc71 represents the harmonic Maass form ITSELF. A harmonic (weak) Maass form f of weight k decomposes UNIQUELY f = f⁺ + f⁻ (Bruinier–Funke, On Two Geometric Theta Lifts, arXiv:math/0212286v4, p. 9, eqs. (3.2a)/(3.2b)): f⁺ is the holomorphic / mock part and f⁻ is the non-holomorphic completion, built from the incomplete-Γ kernel H(w) = e^{−w}Γ(1−k, −2w). The completion f⁻ is a TRANSCENDENTAL PERIOD INTEGRAL — the operand-side "irrepresentable" target this carrier is named after — BUT it is not free: by Lemma 3.1 (p. 10) the lowering operator L_k annihilates f⁺ and sends f⁻ to a holomorphic form whose coefficients carry the UNIVERSAL (−4πn)^{1−k} factor, and Proposition 3.2 (p. 10) packages this as the SHADOW MAP ξ_k(f) := v^{k−2}·conj(L_k f) = R_{−k} v^k conj(f) : H_{k,L} → M^!_{2−k,L^−} with kernel = the holomorphic forms. So the shadow g = ξ_k(f) (a weight-(2−k) UnaryTheta) is a finite exact object, and f⁻ is its EICHLER (period) integral — recoverable from g alone. A harmonic Maass form is therefore DETERMINED by the PAIR (f⁺ holomorphic mock part, g = ξ_k(f) shadow), which is exactly what HarmonicMaass stores. The completion f⁻ is REPRESENTED SYMBOLICALLY (it IS the Eichler integral of the stored shadow) and is NEVER numerically evaluated — the HONEST treatment of a transcendental (no float, no hallucinated digits), NOT a shell. Storing the shadow IS storing the completion. This turns a former operand-side OPEN ("the harmonic-Maass non-holomorphic completion is irrepresentable") into a finite exact reduction by ENLARGING THE CARRIER (the F929 operand program — the dual of an operator-side honest None). Shipped CO-EQUAL in Python AND a 1:1 native C peer srmech_harmonic_maass in the SAME rc (the everything-mirrors / never-split discipline). tools.total 339 → 340 (the public harmonic_maass construction op; the carrier CLASSES HarmonicMaass / MockQSeries add no ToolEntry, like EllRatio / QMat / ThetaSum); ABI stays 3 (a new exported symbol does not bump ABI).
The #9 keystone — Ramanujan's order-3 mock theta is now ONE finite exact carrier. f(q) = Σ_{n≥0} q^{n²} / ((1+q)²(1+q²)²···(1+qⁿ)²) (Zagier, Ramanujan's mock theta functions and their applications [d'après Zwegers and Bringmann–Ono], Séminaire Bourbaki, Astérisque 326 (2009), Exp. 986, p. 145 — the Eulerian series) is a weight-1/2 mock modular form. Its completion ĥ₃(τ) = q^{−1/24}f(q) + R₃(τ) transforms as a weight-1/2 form, where (p. 150) R₃(τ) = (i/√3) ∫_{−τ̄}^{i∞} g₃(z) / √(−i(z+τ)) dz is the EICHLER integral of the weight-3/2 shadow g₃(z) = Σ_{n≥1} (−12/n)·n·q^{n²/24} (the rc70 UnaryTheta). So the #9 mock theta is the pair harmonic_maass(hol='eulerian_f', shadow=g₃), weight 2 − 3/2 = 1/2 — ONE finite exact HarmonicMaass carrier. f(q) is held by the THIN q-series carrier MockQSeries (a leading q-power + a finite GENERATING RULE): the eulerian_f rule emits the exact INTEGER coefficients [1, 1, −2, 3, −3, 3, −5, 7, …] (OEIS A000025; each partial sum is an exact integer power series since ∏(1+qʲ)² has constant term 1, and the n²>N terms drop, so the rule is bounded to any depth), and the qpoly rule holds a finite closed-form mock part exactly.
The API + the exact gates. harmonic_maass(hol, shadow): .weight = 2 − shadow.weight (exact Q; keystone 1/2); .hol / .holomorphic_part (the MockQSeries), .shadow (the UnaryTheta); .xi() returns the shadow (Prop. 3.2: ξ of the pair is its shadow, the holomorphic part being in the kernel); .hol_q_series(N) / .shadow_q_series(N) the exact coefficients to depth. Equality: two HarmonicMaass are equal ⟺ equal hol (exact for a qpoly part, to depth N for a generating rule) AND equal shadow (exact). Gates (all PASS, Python and C): (1) f.weight + shadow.weight == 2 exact Q; (2) hol held exactly to depth (independently cross-checked); (3) shadow held exactly (g₃ weight 3/2 + Zagier coeffs); (4) harmonic_maass(f(q), g₃).xi() == g₃; (5) canonical form (equal ⟺ identical; unequal hol OR shadow ⟹ unequal); (6) the keystone (the #9 mock theta as one weight-1/2 carrier); (7) Python == C parity exact on the hol Eulerian q-series; (8) ratchet-clean.
The honest representability boundary (the operand-side OPEN, named not faked). The pair is exactly decidable when hol is a finitely-decidable carrier (a qpoly closed form OR the eulerian_f generating rule) AND shadow is a UnaryTheta (always decidable). A general mock part with NO finite generating rule stays an HONEST OPEN — harmonic_maass('some_general_mock_part', …) raises rather than fabricate a decision. That boundary is the point of the carrier.
The 1:1 C peer (same-rc). The shadow q-series rides the EXISTING srmech_unary_theta peer (the no-double-copy discipline). The genuinely-NEW computation srmech_harmonic_maass_hol_q_series mirrors the Eulerian f(q) integer q-series over caller-arena srmech_bigint (the same exact-integer substrate as srmech_poly / srmech_unary_theta; no int64 ceiling on the coefficient), byte-identical to Python — built over exact integer power-series algebra (truncated product ∏(1+qʲ)² + integer-series reciprocal invp[m] = −Σ prod[t]·invp[m−t] + the q^{n²} shift). The Python MockQSeries._eulerian_q_series DISPATCHES to it via has_native_harmonic_maass() and falls cleanly to the COMPLETE pure-Python body (the C peer's parity oracle) otherwise. Malloc-free caller-arena (JPL Rule 3; sized to inputs via srmech_harmonic_maass_ws_bound, no compiled-in cap), JPL Power-of-Ten clean (pedantic Release/-DNDEBUG verified); the sign is the Class-K pin-slot (the reciprocal recurrence's subtraction), never an ALU abs(); no math / numpy / libm.
References (MPM-verified at build — the actual arXiv / Astérisque PDFs extracted, equation numbers + page numbers confirmed). Jan H. Bruinier & Jens Funke, On Two Geometric Theta Lifts (arXiv:math/0212286v4): p. 9 eqs. (3.2a)/(3.2b) (the unique f = f⁺ + f⁻ decomposition + the incomplete-Γ kernel H), Lemma 3.1 p. 10 (the (−4πn)^{1−k} completion-coefficient factor), Proposition 3.2 p. 10 (the shadow map ξ_k : H_{k,L} → M^!_{2−k}, kernel = holomorphic forms). Don Zagier, Astérisque 326 (2009), Exp. 986: p. 145 (the order-3 mock theta f(q) Eulerian series), p. 150 (the shadow g₃ + the Eichler integral R₃).
Tests. tests/test_harmonic_maass_rc71.py: the eight exact gates above (weight sum == 2; the f(q) Eulerian coefficients to depth 20 with an independent from-scratch partial-product cross-check; g₃ weight 3/2 + Zagier coefficients; .xi() == g₃; canonical equality + unequal-hol / unequal-shadow; THE KEYSTONE; Python==C parity on the hol Eulerian q-series at several depths, guarded by the native-availability skip; the no-numpy/no-math/no-abs/no-float source guard); the honest-OPEN boundary (an unknown named rule raises); the ToolEntry registration + the tools.total count (== 340, counted over the shipped surface so it is order-independent).
Rosetta: srmech.amsc.harmonic_maass.harmonic_maass is c_dispatched (its hol q-series routes to the srmech_harmonic_maass C symbol; the shadow rides srmech_unary_theta); the two DEBT-bucket ceilings are unchanged.
[0.9.0rc70] - 2026-06-26¶
srmech.amsc.unary_theta — UnaryTheta, the FIRST WEIGHT-GRADED carrier. The operand ladder so far (Q / Poly / QPoly / EllRatio / ThetaSum) is entirely WEIGHT-0: every carrier holds a modular object of weight 0 (a rational function, a balanced theta-quotient). UnaryTheta augments the ladder with a WEIGHT AXIS. A unary theta series is g(τ) = Σ_{n∈support} χ(n)·n^j·q^{(a·n²+b·n)/D}, and its weight is 1/2 + j (half-integral; the classical weight of a unary theta with a degree-j polynomial factor): j = 0 ⇒ weight 1/2, j = 1 ⇒ weight 3/2, j = 2 ⇒ weight 5/2. The weight is an exact Q. Shipped CO-EQUAL in Python AND a 1:1 native C peer srmech_unary_theta in the SAME rc (the everything-mirrors / never-split discipline). tools.total 338 → 339 (the public unary_theta construction op); ABI stays 3 (a new exported symbol does not bump ABI).
The #9 payoff — the mock-theta SHADOW is now representable in-carrier. The harmonic-Maass / mock-theta program (research item #9) needs weight-graded objects: Ramanujan's order-3 mock theta f(q) = Σ_{n≥0} q^{n²}/(−q;q)_n² is a weight-1/2 mock modular form, and its shadow is the weight-3/2 unary theta g₃(τ) = Σ_{n≥1} (−12/n)·n·q^{n²/24} (Zagier, Ramanujan's mock theta functions and their applications [d'après Zwegers and Bringmann–Ono], Séminaire Bourbaki, Astérisque 326 (2009), Exp. 986, p. 150 — the exact shadow of f(q)). A weight-0 carrier simply cannot hold g₃ (its weight is 3/2, not 0), so the shadow was IRREPRESENTABLE in the ladder. UnaryTheta makes it representable at weight 3/2.
The two anchors (the build gates). θ₃ = unary_theta('trivial', j=0, a=1, b=0, D=1, support='all') is Σ_{n∈ℤ} q^{n²} (the Jacobi-triple-product theta): weight 1/2, q_series = [1, 2, 0, 0, 2, …] (cross-checked against the JTP product ∏(1−q^{2k})(1+q^{2k−1})²). g₃ = unary_theta('minus12', j=1, a=1, b=0, D=24, support='positive') is the order-3 mock-theta shadow: weight 3/2, and (factoring out the leading q^{1/24}) q_series = [1, −5, −7, 0, 0, 11, 0, 13, …] — the coefficient ±n of q^{(n²−1)/24} carries the Kronecker sign (−12/n) — the EXACT Zagier coefficients. A j = 2 case gives weight 5/2, showing the axis is general.
The carrier + the 1:1 C peer. .q_series(N) returns the EXACT INTEGER coefficients after factoring out the leading (minimal) q-power (.leading_power() → Q(1, 24) for g₃, Q(0, 1) for θ₃): out[e] = Σ_{n : E(n) = e} χ(n)·n^j. All exact integers — no float, no math, no numpy. The character χ ∈ {−1, 0, +1} (a Character of period M, with 'trivial' / 'minus12' named anchors); its sign is the Class-K pin-slot (a stored ±1), never an ALU abs(). The C peer srmech_unary_theta computes the integer q-series over caller-arena srmech_bigint (the same exact-integer substrate as srmech_poly; n^j is full bignum, no int64 ceiling), byte-identical to Python. The Python .q_series DISPATCHES to it via has_native_unary_theta() and falls cleanly to the COMPLETE pure-Python body (the C peer's parity oracle) otherwise. Malloc-free caller-arena (JPL Rule 3; sized to inputs, no compiled-in cap), JPL Power-of-Ten clean (pedantic Release/-DNDEBUG verified); the n-window is an explicit symmetric integer bound (a Class-J integer floor-sqrt, no float sqrt), so the enumeration is provably complete for any sign of b.
Weight annotation of the existing carriers (the ladder is now weight-consistent). EllRatio.weight is the net (#num − #den)/2 (an exact Q); a BALANCED theta-quotient — the very-well-poised / elliptic case is_elliptic gates on — is weight 0. ThetaSum.weight is 0 (the additive carrier of a balanced elliptic theta rational function). So every carrier below UnaryTheta is explicitly weight-0, and the new weight axis is UnaryTheta.weight = Q(1,2) + j.
Tests. tests/test_unary_theta_rc70.py: the weights (θ₃ = 1/2, g₃ = 3/2, a j=2 case = 5/2, exact Q); θ₃.q_series(20) == [1, 2, 0, 0, 2, …] EXACTLY (with the JTP product cross-check); g₃.q_series reproduces the Zagier coefficients [1, −5, −7, 0, 0, 11, 0, 13, …] EXACTLY at weight 3/2 (the #9 payoff); Python==C parity on .q_series and .weight for both anchors + a broad stress sweep (varying j/a/b/D/support/N, guarded by the native-availability skip); EllRatio.weight / ThetaSum.weight; the no-numpy/no-math/no-abs source guard; the ToolEntry registration + the count.
Rosetta: srmech.amsc.unary_theta.unary_theta is c_dispatched (routes to the srmech_unary_theta C symbol); the two DEBT-bucket ceilings are unchanged.
[0.9.0rc69] - 2026-06-26¶
srmech.amsc.carrier_spectrum — CarrierSpectrum, the OPERAND-side dual of the_one. A first-class CARRIER object (construct / inspect / operate, NOT a diagnostic dict) that READS a carrier element's harmonic occupancy under the shift-Laplacian — the Class-L eigenbasis of the carrier's shape — and exposes the block structure that makes the elliptic key-equation solve genuinely BLOCK-DECOMPOSED (not dense-in-disguise). Where the_one S(σ,θ) is the OPERATOR generator (its shape IS the A–N verbs, the algebra side), CarrierSpectrum is the OPERAND object (its shape is the Class-L shift-Laplacian eigenbasis, the module / excitation side): operator ↔ operand = algebra ↔ module = field ↔ excitation. Shipped CO-EQUAL in Python AND a 1:1 native C peer srmech_carrier_spectrum in the SAME rc (the everything-mirrors / never-split discipline). tools.total 337 → 338 (the public carrier_spectrum op; the carrier CLASS itself adds no ToolEntry, like EllRatio / QMat / ThetaSum); ABI stays 3.
The two orthogonal channels of a carrier element. A carrier element r (an EllRatio prefactor · ∏num θ / ∏den θ) splits EXACTLY into: Channel 1 — Cyclic (Class-I), the σ-eigenspectrum — σ = qshift (x ↦ q·x) is DIAGONAL on monomials (σ(x^k) = q^k·x^k), so the x-exponents present are the σ-eigen-occupancy with eigenvalue q^k; k = 0 is the kernel of the shift-Laplacian L = σ − 1 (the σ-invariant DC / rest mode), and the very-well-poised DOUBLED-BEAT (the x² thetas of the ₈ω₇) shows up as the k = ±2 entries. Channel 2 — Quasi-periodic (Class-L), the p-character blocks — each θ belongs to a p-character class (the net multiplier under x ↦ p·x and y ↦ p·y, Rosengren Eq. 1.6, via the theta-canon), and the σ-INVARIANT block label is that p-character with the q-coordinate STRIPPED; σ traverses ONLY the q-coordinate (Channel 1) and PRESERVES the block (Channel 2) — the channels are orthogonal (verified: σ preserves the block for 4 canonical carriers → 4 distinct blocks).
The non-brute-force key-equation solve (the hard part, proven non-shell). The elliptic Gosper / Zeilberger KEY EQUATION A·σ(Y) − B(x/q)·Y = RHS (Gasper–Schlosser, arXiv:math/0505215; the elliptic_gosper peel produces A, B, C) is a ℚ-linear system for the certificate Y over a theta-product basis. The BRUTE FORCE is ONE dense QMat solve over the WHOLE basis. Because σ preserves the p-character block and multiply-by-A / multiply-by-B(x/q) each SHIFT it by a fixed amount, the operator L(Y) = A·σ(Y) − B(x/q)·Y maps an input block to a SINGLE output block — and since B(x/q) is the σ⁻¹ frame of B in the SAME quasi-periodicity class, block(A) == block(B(x/q)) (the q-stripped, σ-invariant label), so the matrix is block-DIAGONAL. CarrierSpectrum.solve_key_equation GROUPS the basis by block, SOLVES each block as an INDEPENDENT small QMat system, and assembles — replacing the dense n×n Gauss-Jordan with Σ_b (n_b×n_b) over disjoint blocks. It reproduces the dense solve EXACTLY (same Y); solve_key_equation_dense is kept as the brute-force baseline so the no-shell gate asserts block == dense on a concrete key equation, and the per-block report (the partition sizes SUM to the full basis with > 1 block, no block holds the whole basis) PROVES it cannot be the dense solve relabeled.
The 1:1 C peer (same-rc, channel read). srmech_carrier_spectrum mirrors the channel READ (what the public op returns) over the integer theta-exponent lattice — pure composition of the shared srmech_ellbase_* exact-ℚ EllMonomial algebra + srmech_ellbase_theta_canon_full (the quasi-periodicity rewrite) + srmech_ellbase_er_build (the same single copy srmech_elliptic_gosper / srmech_elliptic_recurrence ride). The wire form is the SAME full EllRatio form srmech_elliptic_recurrence_8w7 parses; the cyclic x-exponents + the per-theta q-stripped block-label rows come back, and the Python trusts the native result ONLY after the pure rebuild reproduces the SAME spectrum byte-for-byte (the channels are a pure exponent-lattice read). Malloc-free caller-arena (JPL Rule 3; sized to inputs, no compiled-in cap), JPL Power-of-Ten clean (pedantic Release/-DNDEBUG verified); the q-strip / sign is the Class-K pin-slot, never abs(); no math / numpy / libm. The block-DECOMPOSED solve_key_equation METHOD stays Python-side — it rides the additive ThetaSum carrier's full arithmetic (multiply / shift_x / coordinate-emit), whose C peer is OWED (the C srmech_thetasum surface today exposes only is_zero); the genuine elliptic-Zeilberger BUILT on solve_key_equation (gate ©) is the unblocked NEXT rc.
Reference (the harmonic-shape framing; MPM-verified at build). Hjalmar Rosengren, Elliptic Hypergeometric Functions (arXiv:1608.06161v3 [math.CA]), §1.3 Lemma 1.3.2 (the period-annulus pole/zero count that bounds each block's degree) + §1.4 Eq. (1.12) (the Weierstrass three-term relation the ThetaSum block-reduction runs); the key equation is Gasper–Schlosser (arXiv:math/0505215).
Tests. tests/test_carrier_spectrum_rc69.py: σ preserves the block for 4 canonical carriers → 4 distinct blocks (the orthogonality lever); the ₈ω₇ cyclic exposes the doubled-beat (k = ±2) + the blocks are non-trivial (12 blocks, all 14 thetas placed, distinct x-degrees); THE CRITICAL GATE — on a concrete key equation the block solve_key_equation reproduces the dense solve EXACTLY and recovers the known certificate, AND the solve is genuinely per-block (> 1 block, per-block unknowns SUM to the basis, no block holds the whole basis — it cannot be the dense solve relabeled); the public-op end-to-end read; operand coercion; Python==C parity on the spectrum (guarded by the native-availability skip); the no-numpy/no-math/no-abs source guard; the ToolEntry registration.
Rosetta: srmech.amsc.carrier_spectrum.carrier_spectrum is c_dispatched (routes to the srmech_carrier_spectrum C symbol); the two DEBT-bucket ceilings are unchanged.
[0.9.0rc68] - 2026-06-26¶
srmech.amsc.elliptic_recurrence.elliptic_recurrence_8w7 — the ELLIPTIC Σ-row ORDER-1 RECURRENCE op for the Frenkel–Turaev ₈ω₇ summation, a STRUCTURAL (beat-decomposition) finder. The first elliptic Σ-row op that produces a definite-sum RECURRENCE (sitting between the rc65 indefinite-summation elliptic_gosper antidifference and a future elliptic-WZ proof op), specialised to the very-well-poised ₈ω₇ keystone. Shipped CO-EQUAL in Python AND a 1:1 native C peer srmech_elliptic_recurrence_8w7 in the SAME rc (the everything-mirrors / same-rc discipline). tools.total 336 → 337; ABI stays 3 (a new exported symbol does not bump ABI).
What it does (recognize-decompose-construct, NOT a coefficient solve). Given the ₈ω₇ summand's term ratio t(n+1)/t(n) = r(x) (x = qⁿ) — the very-well-poised core θ(aq²x²)θ(ax)/[θ(ax²)θ(qx)] over five Pochhammer pairs θ(ux)/θ(aqx/u) with the balancing bcde = a²q^{n+1}, an EllRatio — it (1) RECOGNIZES the ₈ω₇ structure, (2) DECOMPOSES it into the base a plus the three FREE params [b, c, d] (the beat decomposition: the unique magnitude-2 den factor θ(ax²) gives a; the magnitude-1 den factors give aq·real_base⁻¹ = u; the (q)-factor and the y-carrying n-dependent params drop out), and (3) CONSTRUCTS the order-1 recurrence coefficient ρ(n) (an EllRatio in y = qⁿ, 4 num + 4 den thetas) from the elementary symmetric functions s₂ = {bc, bd, cd}, s₃ = bcd — num endpoints {aq} ∪ {aq/bc, aq/bd, aq/cd}, den {aq/b, aq/c, aq/d} ∪ {aq/bcd}. The recurrence is f(n+1) = ρ(n)·f(n) (coeffs = [-ρ, 1]). The construction IS the answer (decompose-and-compute); a coefficient nullspace solve is the WRONG method and is provably dead for the elliptic case (the rc66 obstruction: the certificate's Lagrange coordinates are transcendental theta-values, not rationals) — so this op constructs ρ from the very-well-poised shape, it does not search for it (the anti-brute-force discipline).
Verified (the gate, not a shell). The op returns ρ only after VERIFYING ρ(n) == f(n+1)/f(n) for the actual ₈ω₇ DEFINITE sum f(n) = (aq, aq/bc, aq/bd, aq/cd; q,p)_n / (aq/b, aq/c, aq/d, aq/bcd; q,p)_n (Warnaar, Constr. Approx. 18 (2002) 479–502, arXiv:math/0001006, Corollary 2.2 — the closed product form is the INDEPENDENT oracle) to < 1e-9 for n = 1, 2, 3, evaluated by the carrier's OWN exact-ℚ truncated-theta eval_trunc (NOT a standalone numeric theta). A ratio that is NOT a canonical ₈ω₇ (unbalanced, no very-well-poised quadratic core, or ≠ 3 free params) or a ρ that fails the gate → honest None (the out-of-class residue). The reference is MPM-verified at build (Warnaar Cor 2.2, arXiv:math/0001006).
The 1:1 C peer. srmech_elliptic_recurrence_8w7 mirrors the recognize-decompose-construct over the integer theta-exponent lattice — pure composition of the shared srmech_ellbase_* exact-ℚ EllMonomial algebra + srmech_ellbase_er_build (the same single copy srmech_elliptic_gosper / srmech_ellratio_is_elliptic / srmech_elliptic_lagrange_basis ride). The wire form mirrors srmech_elliptic_gosper but adds the y interned index (the recurrence axis). The Python op DISPATCHES to it via has_native_elliptic_recurrence_8w7() and trusts a native ρ ONLY after a byte-for-byte rebuild against the pure-Python construction AND re-running the ₈ω₇ gate in exact ℚ (the rc67 hardening lesson — do NOT trust the C); a has = 0 (not a ₈ω₇) falls through to the COMPLETE pure-Python body. The "magnitude 2 / magnitude 1" x-power test is a Class-K parity branch (e == mag || e == -mag), never abs()/fabs(). Malloc-free caller-arena (JPL Rule 3; sized to inputs, no compiled-in cap), JPL Power-of-Ten clean; no math / numpy / libm / abs().
Tests. tests/test_elliptic_recurrence_8w7_rc68.py: the op reproduces ρ(n) on the canonical ₈ω₇ (verified against the closed product form at an INDEPENDENT sample point, n = 1, 2, 3); None on unbalanced + balanced-but-wrong-shape + zero ratios; Python==C byte-exact parity on the ρ EllRatio output (drive both paths, compare element-for-element, guarded by the native-availability skip); the operand-dict round-trip; the no-numpy/no-math/no-abs source guard; the ToolEntry registration.
Rosetta: srmech.amsc.elliptic_recurrence.elliptic_recurrence_8w7 is c_dispatched (routes to the srmech_elliptic_recurrence_8w7 C symbol); the two DEBT-bucket ceilings are unchanged.
[0.9.0rc67] - 2026-06-26¶
srmech_elliptic_lagrange_basis — the 1:1 native C peer of the rc66 srmech.amsc.ellbase.elliptic_lagrange_basis carrier op, discharging the owed same-rc C parity. A C-MIRROR PARITY build, NOT a new algorithm: it reproduces the EXISTING, already-shipped pure-Python carrier byte-for-byte. The rc66 op shipped Python-only; the everything-mirrors discipline (a C peer is delivered WITH Python in the same rc, never split, never owed as backlog) requires its native twin — this is it.
What it mirrors. For k = len(points) interpolation nodes and a multiplier t, the C builds the same k elliptic Lagrange basis EllRatios of the k-dimensional space V_t = {f analytic on ℂ* : f(p·z) = t·z^{-k}·f(z)} (Rosengren, Elliptic Hypergeometric Functions, arXiv:1608.06161v3 §1.3 Corollary 1.3.5). For each i: the balancing point v_i = (−1)^k·t / ∏_{j≠i} u_j and L_i = EllRatio(num = [θ(z·u_j^{-1}) : j ≠ i] + [θ(z·v_i^{-1})]) with the default unit prefactor, fully variable-agnostic via var (_X summation axis / _Y dual recurrence axis).
Pure composition of the shared kernels (the everything-mirrors no-double-copy discipline). The C peer composes the shared srmech_ellbase_* exact-ℚ EllMonomial algebra (mul / inv) + srmech_ellbase_er_build (the EllRatio.__init__ mirror: canonicalize each theta, fold its prefactor, cancel matching thetas, sort the survivors) — the SAME single copy srmech_ellratio_is_elliptic and srmech_elliptic_gosper ride. The wire form mirrors srmech_ellratio_is_elliptic: the interned symbol-table dimension + the var / p interned indices + the flat exact-ℚ srmech_bigint coeff arrays + the flat int32 exponent rows; the k basis EllRatios come back as the per-element prefactor coeff arrays + theta counts + canonical exponent rows. The (−1)^k sign is a Class-K parity branch (an int ±1), never abs()/fabs(). Malloc-free caller-arena (JPL Rule 3; sized to inputs, no compiled-in cap), JPL Power-of-Ten clean; no math / numpy / libm.
Dispatch + parity. elliptic_lagrange_basis DISPATCHES to the C peer via has_native_elliptic_lagrange_basis() and trusts it 1:1 (the C basis EQUALS the Python basis byte-for-byte); the pure-Python _elliptic_lagrange_basis_py body STAYS as the COMPLETE alternative + the parity oracle. tests/test_ellbase_c_parity.py asserts the returned basis EllRatios are byte-exact equal between the Python-only path and the native path, for BOTH var=_X and var=_Y, across k ∈ {1, 2, 3} and symbolic + specialized points (guarded by the native-availability skip).
Rosetta: srmech.amsc.ellbase.elliptic_lagrange_basis moves bignum_reference → c_dispatched (it now dispatches to C). tools.total STAYS 336 (a NEW exported symbol, not a new ToolEntry); a new exported symbol does NOT bump ABI → ABI stays 3.
[0.9.0rc66] - 2026-06-26¶
srmech.amsc.ellbase.elliptic_lagrange_basis — the degree-d elliptic Lagrange interpolation basis, now VARIABLE-AGNOSTIC (the dual x / y axes). A complete, verified CARRIER primitive of the elliptic F929 row (a peer of the rc59/rc60/rc62 EllMonomial / Theta / EllRatio / ThetaSum carriers), exact over the modified-theta algebra — no float, no abs() (sign is the Class-K pin-slot), no math / numpy. tools.total STAYS 336 (a carrier foundation, not an engine ToolEntry); ABI 3 (Python carrier, C peer owed by the everything-mirrors discipline).
What it is. For k = len(points) interpolation nodes and a multiplier t, it returns the Lagrange basis of the k-dimensional space V_t = {f analytic on ℂ* : f(p·z) = t·z^{-k}·f(z)} of higher-order theta functions (Rosengren, Elliptic Hypergeometric Functions, arXiv:1608.06161v3 §1.3 Corollary 1.3.5 + Eq. (1.5)). Each L_i places its k zeros at {u_j : j ≠ i} plus the BALANCING point v_i = (−1)^k·t / ∏_{j≠i} u_j, so every L_i lands in the SAME V_t (multiplier EXACTLY t·z^{-k} via the Class-K pin-slot prefactor θ(p·z/c) = −(c/z)·θ(z/c)), and the k elements span V_t — the linear ansatz any f ∈ V_t decomposes over as Σ_i c_i·L_i.
The var parameter (default _X). var=_Y builds the DUAL basis on the recurrence axis y = qⁿ, fully mirroring the summation-axis construction one variable up — the operand x/y duality made constructive. (The elliptic creative-telescoping certificate factors as R(x,y) = R_x(x)·R_y(y), both factors balanced/elliptic in their own variable, so the recurrence-coefficient space lives in a V_{t_y} spanned by elliptic_lagrange_basis(…, var=_Y) exactly as the certificate numerator lives in V_{t_x} on x.)
Verified (numpy-absent), both axes: multiplier EXACTLY t·z^{-k} under z ↦ p·z; the balancing product-of-zeros (−1)^k·t; Lagrange zero-placement L_i(u_j) = 0 for j ≠ i and ≠ 0 at u_i via the carrier's exact eval_trunc (θ(1;p) = 0 exactly, so the zeros are exact, not truncation artifacts); the k = 1 and empty-points edge cases. 12 tests (7 on x, 5 on the dual y).
Scope note (the no-partial-shell discipline). This rc ships ONLY the complete, verified carrier. The order-≥1 elliptic_zeilberger FINDER is NOT in this rc: its undetermined-coefficient/nullspace approach was proven impossible (a coefficient-field obstruction — the certificate's Lagrange coordinates are transcendental theta-values, not rationals, so no exact-ℚ linear solve can find it), redirecting the finder to the EXPLICIT structural Gosper–Petkovšek construction (Gasper–Schlosser arXiv:math/0505215 Eq. (3.5)). The verified theta-product reduction engine that decides such certificates exactly stays on the feature branch for the rc that lands the complete engine op.
[0.9.0rc65] - 2026-06-24¶
srmech.amsc.elliptic_gosper.elliptic_gosper — the GENUINE STRUCTURAL elliptic-Gosper finder, replacing the rc61 brute-force shell (the FIRST engine op of the ELLIPTIC F929 reduction row, now on the genuine k-dependent Frenkel–Turaev keystone). The rc61 op had TWO fatal bugs, EITHER of which alone returned None on the genuine keystone: (1) the FINDER was a brute-force _candidate_certificates ENUMERATE-and-test (it never constructed the right (B/C) frame); (2) the VERIFIER was the eval_trunc gap-check whose own docstring admitted it must DECLINE genuine theta telescopers (a truncated modified-theta product never reaches exact 0). Both are fixed: the genuine decompose-and-compute structural finder + the exact additive ThetaSum.is_zero verifier.
The structural algorithm (decompose-and-compute, NOT enumerate-and-test). (1) Row gate r.is_elliptic() (the balancing / very-well-poised predicate, Gasper–Schlosser Eq. (2.4)); an unbalanced ratio is out of the row → None. (2) PEEL the q-shift coboundary (_peel_coboundary) to the theta-Gosper–Petkovšek normal form r = (A/B)·(σC/C) (σ : x ↦ q·x): a denominator θ(D) is a coboundary (C) factor iff its q-shifted partner θ(σD) = θ(D·q^{e_x(D)}) is a numerator factor — the hidden q-shift fiber (the spatially-absent shift orbit) made explicit; this is the STRUCTURAL decomposition where the rc61 find-gap lived, NOT the y-solve. (3) SOLVE the elliptic Gosper equation via the Weierstrass three-term relation (Rosengren arXiv:1608.06161 §1.4 Eq. (1.12)): the certificate is R = (B(x/q)/C)·y with the CONSTANT y = (c/a)/[θ(ba)θ(b/a)] extracted from the x-params a, b, c of A, B(x/q), C — since the three-term relation gives A − B(x/q) = (a/c)·θ(ba)θ(b/a)·C. Theta.canonicalize fixes each x-param only up to the α ↔ α⁻¹ chiral endianness, so the ≤ 8 handednesses of (a,b,c) are tried and the exact verifier picks the one that closes R(qx)·r − R == 1 (a Class-K sign / Class-C chirality resolution — the SHAPE is determined by the GP factoring; only the endianness is free; the genuine keystone's certificate sits in the FULLY-FLIPPED chiral-inverse reading). The exact verifier is the additive ThetaSum.is_zero (the rc62 carrier), NOT a converging eval — the same no-hallucination, exact-proof-object standard the §76 gosper / zeilberger / wz_certificate ops hold.
1:1 C peer srmech_elliptic_gosper REWRITTEN to mirror the peel-solve structure (shipped Python+C in the SAME rc, never split). The rc61 C was the OLD scalar brute-force accelerator (constant-ratio geometric core only); it is replaced by a genuine C mirror that takes the full EllRatio wire form (the interned symbol-table + flat int32 exponent rows + flat exact-ℚ srmech_bigint coeff arrays, the same convention as srmech_ellratio_is_elliptic), peels the coboundary over the integer theta-exponent lattice, extracts the x-params, builds the ≤ 8 endianness candidates, and verifies each via the native srmech_thetasum_is_zero — byte-exact parity with the Python certificate. It builds on the shared srmech_ellbase_* (canonicalize / monomial algebra) + srmech_thetasum_* (is_zero) kernels (the everything-mirrors no-double-copy discipline). Malloc-free caller-arena (JPL Rule 3; sized to inputs, no compiled-in cap), JPL Power-of-Ten clean; no abs()/fabs() (Class-K sign branch), no math / numpy.
Dead-code cleanup. The rc61 _theta_dispersion, _x_coeff_monomial, _gap_to_one, _VERIFY_POINTS, _VERIFY_TRUNC, _MAX_DISPERSION, and the cascade.magnitude import — all unused by the genuine decision path — are removed.
ENGINE op → tools.total STAYS 336 (the elliptic_gosper ToolEntry already existed at rc61); ABI 3 (the srmech_elliptic_gosper wire-format change is a same-symbol rewrite, not a new exported function, and the genuine engine's certificate is re-verified in exact ℚ on return). Verified (numpy-absent): the GENUINE Frenkel–Turaev keystone → the certificate Rc EXACTLY (no candidate-family enumeration); a non-elliptic (unbalanced) ratio → None; the zero ratio → None; the pure-constant ratio (no x-dependent theta) is out of the single-Weierstrass class → None (the genuine finder does not certify the trivial geometric constant the rc61 shell did). This is the genuine elliptic_gosper rung; the row continues with the genuine elliptic_zeilberger / elliptic_wz_certificate built on this now-proven structural pattern.
[0.9.0rc64] - 2026-06-24¶
srmech_ellbase_* / srmech_ellratio_is_elliptic — the 1:1 native C peer of the rc59/rc60 EllRatio / Theta / EllMonomial carrier family, shipped Python+C in the SAME rc (discharging the rc59 EllBase + rc60 EllRatio Python-only splits). The next rung of the carrier-C foundation that makes the elliptic candidate-search native — the rung after ThetaSum-C (rc63). A C-MIRROR PARITY build, NOT a new algorithm: it mirrors the EXISTING, already-shipped Python carriers byte-for-byte.
ONE shared kernel, two consumers (the everything-mirrors no-double-copy discipline). The rc63 srmech_thetasum.c already mirrored Theta.canonicalize + the EllMonomial exact-ℚ monomial algebra as static helpers (for is_zero). Those kernels are now PROMOTED to a new c/src/srmech_ellbase.c as shared srmech_ellbase_* symbols (the exact-ℚ two-bigint algebra, the dense-exponent monomial mul/inv/div/sqrt + the _sort_key tuple-order compare, and Theta.canonicalize in both an argument-only and a full-prefactor form), declared in an internal cross-TU header srmech_ellbase_internal.h. srmech_thetasum.c now #includes it and calls the SAME single copy (thin ts_* → srmech_ellbase_* aliases keep its call sites unchanged) — so ThetaSum and EllRatio share ONE Theta-C kernel, not two. Pure promotion of internal helpers → ABI stays 3; the rc63 srmech_thetasum_is_zero wire-format is unchanged.
The CRUCIAL new decision: srmech_ellratio_is_elliptic — the COMPLETE balancing / very-well-poised predicate is_elliptic() == (pshift() == self). The term-ratio is a genuine elliptic function (a function on the elliptic curve ℂ*/⟨p⟩) IFF it is invariant under the period shift x ↦ p·x. The C reproduces the Python decision byte-for-byte: it period-shifts the prefactor + every theta argument (x ↦ p·x: a monomial gains p^{its x-exponent} — so the interned symbol table MUST carry p and x even when the canonical form carries neither, else the shift is a silent no-op), RE-CANONICALIZES (the Theta.canonicalize quasi-periodicity + inversion rewrites fold each prefactor exactly, incl. the rc59-fixed negative-odd-k sign), cancels matching canonical thetas between numerator and denominator, sorts the survivors, and compares the canonical (prefactor, num-multiset, den-multiset) to self's EXACTLY. NOT a bounded/numeric shell — no convergence threshold on any decision path. EllRatio.is_elliptic DISPATCHES to it via has_native_ellratio() and trusts it unconditionally (a 1:1 mirror); the pure-Python _is_elliptic_py is the COMPLETE alternative + the parity oracle. The qshift/pshift/*/inv carrier compute methods are reachable through it (the same canonicalize-fold-cancel-sort construction).
Parity is THE gate (tests/test_ellbase_c_parity.py). The C verdict EQUALS the Python verdict on: a STRICT-elliptic ratio (True), a balanced-but-not-strict boundary case, a non-elliptic ratio (False); the qshift/pshift/*/inv round-trips; canonicalize on theta products incl. negative-odd-k sign cases; plus a randomized fuzz (~1000 cases) with ZERO C/Python divergence. The rc63 tests/test_thetasum_c_parity.py stays GREEN after the Theta-C promotion (no regression). has_native_ellratio() confirms is_elliptic is decided by the C peer (not a silent Python fallback).
Carrier → tools.total STAYS 336 (no ToolEntry — srmech_ellratio_is_elliptic is a carrier-decision peer, like srmech_thetasum_is_zero / srmech_qmat_rref). Adding new symbols / promoting internal helpers does NOT bump ABI → ABI stays 3. Malloc-free caller-arena (JPL Rule 3; sized to inputs, no compiled-in cap), JPL Power-of-Ten clean (no goto / ≤60-line functions / ≥2 asserts per non-exempt function / no multi-line macros); pedantic -Wall -Wextra -Werror -pedantic clean (Release -DNDEBUG + Debug). numpy / import math / abs()-free; Class-K sign (int ±1).
[0.9.0rc63] - 2026-06-24¶
srmech_thetasum_* — the 1:1 native C peer of the rc62 ThetaSum carrier's is_zero, shipped Python+C in the SAME rc (discharging the rc62 Python-only split). This is the FOUNDATION that makes the elliptic candidate-search native/fast (the first of ThetaSum-C → EllRatio-C → EllBase-C → a structure-aware elliptic_gosper), per the everything-mirrors / same-rc discipline. It is a C-MIRROR PARITY build, NOT a new algorithm: srmech_thetasum_is_zero reproduces the pure-Python ThetaSum.is_zero decision byte-for-byte — the COMPLETE EXACT Weierstrass three-term reduction partitioned by quasi-periodicity class (Rosengren arXiv:1608.06161v3 §1.4 Eq. 1.12 + §1.3 Lemma 1.3.2), NOT a bounded/numeric shell. Where Python honestly returns False on a shape outside the clean ±-pair form the carrier reduces, the C returns the SAME False (sound, never false-accept; never a converging eval).
The decision mirror. The cleared numerator terms ride a numpy-free ctypes bridge: an interned symbol table (the distinct symbols, sorted by NAME so the C dense exponent vector reproduces the EllMonomial._sort_key tuple-of-(symbol,exp)-pairs compare — a plain index-walk would be WRONG), the p/x/y indices, the per-term theta counts, the flat monomial coeff arrays (each an exact-ℚ num/den over srmech_bigint, byte-identical at ANY magnitude, OVERFLOW-not-wrap) + the flat int32 exponent rows. The C reproduces every step: Theta.canonicalize (quasi-periodicity + inversion), the quasi-periodicity-class key (_net_period_multiplier_exps), the ±-pair recovery via the exact monomial-sqrt midpoint test, the two build-verified _canon_pair orientation rules, and the strictly-decreasing three-term rewrite (_three_term_rewrite) with its order-preserving partner-selection + the order-INDEPENDENT (multiset) like-term combine — Σ-pairs left in the live emission order so the rewrite scan matches Python exactly. Malloc-free (JPL Rule 3): every working monomial / theta / term / rterm + bigint scratch carved from the caller arena, sized to the input (n_terms, n_thetas, n_syms) — no compiled-in math cap. Class-K sign (int ±1, never abs()). The pure-Python ThetaSum._is_zero_py is the COMPLETE alternative + the parity oracle; is_zero DISPATCHES to the C peer via has_native_thetasum() and trusts it unconditionally (it is a 1:1 mirror).
Parity is THE gate (tests/test_thetasum_c_parity.py). The C verdict EQUALS the Python verdict on: the GENUINE theta-telescoper keystone residual R(qx)·r − R − 1 ≡ 0 (the c=qb Weierstrass construction from the closed feat/srmech-rc63-elliptic-gosper-genuine branch — the case ThetaSum exists to decide; C == Python == True); perturbed certificates (× a scalar / × a parameter → False); the zero / one / degenerate-θ(1;p) / odd-single-theta cases; the Weierstrass three-term identity + a broken-weight variant; the quasi-periodicity two-class grouping; and the shifts. A randomized fuzz (≈13k cases — three-term identities + their sums, broken variants, random theta products incl. θ(1) collapses, scaled combos) shows zero C/Python divergence. has_native_thetasum() confirms the keystone is decided by the C peer (not a silent Python fallback).
Carrier → tools.total STAYS 336 (no ToolEntry — srmech_thetasum_is_zero is a carrier-decision peer, like srmech_qmat_rref). Adding new symbols does NOT bump ABI → ABI stays 3. JPL Power-of-Ten clean (no goto / no malloc / ≤60-line functions / ≥2 asserts per non-exempt function / no multi-line macros); pedantic -Wall -Wextra -Werror -pedantic clean (Release -DNDEBUG + Debug). numpy / import math / abs()-free.
[0.9.0rc62] - 2026-06-24¶
srmech.amsc.thetasum.ThetaSum — the ADDITIVE theta-function CARRIER that unblocks GENUINE elliptic creative telescoping (rc62 of the ELLIPTIC F929 arc; the additive layer over the rc59 Theta / rc60 EllRatio). Where EllRatio carries a single MULTIPLICATIVE theta-quotient and is NOT additively closed, genuine creative telescoping's residual Σ_j a_j(n)·F(n+j,k) − (G(n,k+1) − G(n,k)) is a SUM / DIFFERENCE of theta-quotients — undecidable in EllRatio (the boundary that forced rc61 elliptic_gosper and the closed partial elliptic_zeilberger to honestly hit None on the k-dependent case). ThetaSum is the cleared rational theta-function (ℚ(q,p)-linear SUM of theta-products) / (single theta-product denominator) that closes that gap. A CARRIER (peer of EllRatio/QMat/Poly/TriPoly) — re-exported as srmech.amsc.ThetaSum; NO ToolEntry, NO new MCP param type → describe()["tools"]["total"] stays 336, ABI 3. The 1:1 C peer srmech_thetasum_* is OWED by the everything-mirrors discipline (the rc62-prefix backlog, like EllBase/EllRatio/QMat); the pure-Python body is the COMPLETE alternative + the parity oracle.
API. from_ellratio(r) (lift a single-term EllRatio); zero()/one(); __add__/__sub__/__neg__ (common-denominator numerator add/subtract), scalar_mul (exact Q), __mul__ (numerator/denominator products); shift_x() (σ_x: x→q·x) / shift_y() (σ_y: y→q·y) — the per-summation-symbol elliptic shift, generalising rc60's _shift; is_zero / __eq__ (= (self−other).is_zero); eval_trunc (exact-ℚ truncated-product CONVERGENCE ORACLE, used by TESTS only, NOT by the decision). numpy / import math / abs()-free; Class-K sign only.
is_zero is EXACT — a SYMBOLIC Weierstrass three-term-relation reduction, NEVER a convergence threshold (the rc61 / §76 no-hallucination standard). A genuine theta identity (terms with DIFFERENT theta multisets, e.g. the Weierstrass relation) is NEVER exactly 0 at any finite eval_trunc depth — a truncated modified-theta product only CONVERGES — so the decision MUST be symbolic, not a witnessed eval. The pipeline: (1) clear to the numerator (nonzero theta denominator ⟹ self==0 ⟺ numerator≡0); (2) group terms by QUASI-PERIODICITY CLASS (the net multiplier monomial under x↦p·x AND y↦p·y, via the rc59 Theta.canonicalize quasi-periodicity rewrite) — different-quasi-periodicity theta-products are linearly independent over ℚ(q,p), so the numerator is ≡0 iff EACH class vanishes; (3) within a class, recover each theta-product's ±-pair structure θ(α·β^±)=θ(αβ)θ(α/β) (the pairing recovered by the exact MIDPOINT / geometric-mean test), then reduce by the EXACT Weierstrass three-term relation, driving every summation-symbol pair to a single class reference via a rewrite that strictly lowers the largest such midpoint (hence TERMINATES, no cycling) and combining like terms exactly in the carrier (inversion prefactors folded exactly via the two build-verified orientation rules θ(α·β^±)=θ(α·(1/β)^±) and θ(u·v^±)=−(u/v)θ(v·u^±)). The class is ≡0 IFF every normal-form coefficient cancels to Q(0) — exact symbolic carrier algebra, no evaluation. A theta-product outside the clean ±-pair shape this carrier reduces is honestly reported NOT-zero rather than accepted on a converging witness.
The two load-bearing theorems are MPM-verified from the ACTUAL source PDF (extracted + read in full, equation numbers + statement confirmed — NOT a training-data attribution): Hjalmar Rosengren, "Elliptic Hypergeometric Functions" (Lectures at OPSF-S6, College Park MD, 11–15 July 2016), arXiv:1608.06161v3 [math.CA], 20 Jun 2017. (1) The theta ADDITION FORMULA is the Weierstrass three-term theta relation, §1.4 Eq. (1.12): θ(ax^±,bc^±;p) = θ(bx^±,ac^±;p) + (a/c)·θ(cx^±,ba^±;p) (with the §1.2 shorthand θ(uv^±)=θ(uv)θ(u/v)), proved there from scratch via Liouville's theorem; implemented as ThetaSum.three_term(a,b,c) (the certificate-shaped identity whose is_zero is True). (2) The DEGREE BOUND is the Fundamental Theorem of Elliptic Functions, §1.3 Lemma 1.3.2: "Let f be multiplicatively elliptic with period p. Then, f has as many poles as zeroes, counted with multiplicity, in each period annulus" — with the Liouville corollary (a non-constant elliptic function must have poles) that a pole-free theta combination of bounded degree which vanishes is identically zero (cf. the §1.6 Prop. 1.6.1 theta-interpolation dimension count). Both forms lodged in the module docstring with where/how verified.
Verified (numpy-absent): ThetaSum.three_term(a,b,c).is_zero is True (the exact symbolic Weierstrass reduction — no eval), and a deliberately-broken weight → False; the addition formula holds as exact ThetaSum equality; shift_x/shift_y preserve a known identity (it holds for any summation point); a 2-class (x-pairs + y-pairs) sum is zero iff both classes vanish (break either → nonzero); the eval_trunc oracle CONFIRMS a true identity merely CONVERGES toward 0 (NOT exactly 0 at finite depth — the very reason is_zero is symbolic). tools.total 336, ABI 3, no numpy / import math / abs(). Foundation for the GENUINE rc63+ elliptic_gosper / elliptic_zeilberger / elliptic_wz_certificate rebuilt on the additive carrier.
[0.9.0rc61] - 2026-06-24¶
srmech.amsc.elliptic_gosper.elliptic_gosper — the ELLIPTIC analog of Gosper's indefinite hypergeometric summation, the FIRST ENGINE op of the ELLIPTIC F929 reduction row (the top of the base-axis degeneration tower elliptic → q → ordinary; the elliptic analogue of gosper / q_gosper, ONE algebra up). It builds on the rc59 ellbase theta-factor foundation + the rc60 EllRatio term-ratio carrier.
Reference (MPM-verified at build — the actual arXiv PDF extracted, authors + title + venue + year confirmed, NOT a training-data attribution): George Gasper and Michael Schlosser, "Summation, transformation, and expansion formulas for multibasic theta hypergeometric series," Adv. Stud. Contemp. Math. (Kyungshang) 11, no. 1 (2005), 67–84 (arXiv:math/0505215). The abstract states the results are derived "using indefinite summation" — the theta / elliptic analogue of Gosper's indefinite-summation telescoping. The elliptic balancing (very-well-poised) condition that gates the row is Gasper–Schlosser Eq. (2.4) a₁a₂…a_{r+1} = (b₁…b_r)q, making g(x) = z·∏θ(a_kqˣ;p)/θ(b_kqˣ;p) an elliptic (doubly-periodic) function — exactly the rc60 EllRatio.is_elliptic predicate (g invariant under the period shift x ↦ p·x). Secondary anchor (already cited by ellbase): S. O. Warnaar, Constr. Approx. 18 (2002) 479–502; keystone = the Frenkel–Turaev ₁₀E₉ sum.
Signature + math. elliptic_gosper(r) takes the elliptic-hypergeometric term ratio t(n+1)/t(n) = r(x) (x = qⁿ; the summation shift σ : x ↦ q·x) — an EllRatio (a theta-quotient ∏θ(αx;p)/∏θ(βx;p) over an exact-ℚ monomial prefactor; an EllMonomial / Theta is lifted). It gates on the balancing predicate (r.is_elliptic(); an unbalanced ratio is out of the row → None), then decides whether t(n) has an elliptic-hypergeometric antidifference T(n) = R(x)·t(n) (so T(n+1) − T(n) = t(n), the sum telescopes Σ_{a}^{b} t = T(b+1) − T(a), and the elliptic Gosper equation R(qx)·r(x) − R(x) = 1 holds). If so it returns the certificate R as {"prefactor": {"coeff": (num, den), "exps": {sym: exp}}, "num": [{sym: exp}, …], "den": [{sym: exp}, …], "certificate": EllRatio}; else None.
Exactness. Exact over the modified-theta algebra; the theta-quotient carrier is multiplicatively but NOT additively closed, so the additive Gosper equation is decided structurally and CERTIFIED in exact ℚ via EllRatio.eval_trunc (the Class-K magnitude of the gap to 1 — never an ALU abs()). A certificate is accepted ONLY when that gap is EXACTLY zero in exact ℚ — which holds iff the residual carries no surviving theta (the elliptic-geometric core, whose truncated value is exact at any depth). A candidate whose gap merely converges is REJECTED, not accepted: a certificate is an exact proof object, never a numerically-converging witness — the same no-hallucination, exact-verification standard the §76 gosper/zeilberger/wz_certificate ops hold. Genuine single-x theta telescopers (which converge but do not vanish at finite depth, and live in the multi-x q^{2n} lattice — a future carrier extension) therefore return honest None. No float, no abs(), no math / numpy. 1:1 C peer srmech_elliptic_gosper (c/src/srmech_elliptic_gosper.c, prototype in c/include/srmech.h, ctypes binding in _native.py) is a bounded-scope accelerator (the srmech_q_gosper precedent): it completes the canonical elliptic-GEOMETRIC core natively — a CONSTANT term ratio r = z = z_num/z_den (no theta factors, scalar prefactor), certificate R = z_den / (z_num − z_den) (the elliptic analogue of the ordinary / q-geometric R = 1/(z − 1)), byte-identical to Python at any magnitude (full bignum, caller-arena, malloc-free, JPL-clean) — and DECLINES the rest (out_has = 0 → the Python op re-runs its COMPLETE pure-Python path; a has=0 is never a definitive "no certificate"). The full single-x theta-telescoper decision is the owed everything-mirrors backlog (and is mathematically empty in the single-x lattice — genuine elliptic telescopers live in a multi-x q^{2n} lattice, a future carrier extension).
ENGINE op (the FIRST of the ELLIPTIC row) → tools.total 335 → 336, ABI 3 (additive symbol). New MCP param type EllRatio (a scalar (num, den) / int coerces to the constant-ratio EllRatio). Verified: r = 3/2 → certificate R = 2 with R(qx)·r − R = 1 exact (native + pure, byte-identical); a balanced-but-not-summable theta ratio → None; a non-elliptic (unbalanced) ratio → None (out of row). This is rc61 of elliptic_gosper → rc62 elliptic_zeilberger → rc63 elliptic_wz_certificate (the elliptic engine mirroring the §76 telescope trio one algebra up).
[0.9.0rc60] - 2026-06-24¶
srmech.amsc.ellbase.EllRatio — the elliptic-hypergeometric TERM-RATIO carrier + the balancing / very-well-poised predicate (rc60 of the ELLIPTIC F929 arc; builds on the rc59 EllMonomial/Theta foundation). EllRatio is an exact prefactor · ∏(num θ) / ∏(den θ) of Theta factors over an EllMonomial prefactor — the elliptic analogue of a q-term-ratio, one algebra up. On construction every theta is canonicalized (Theta.canonicalize), its prefactor folded into the global prefactor, matching θ cancelled between numerator and denominator, and the surviving multisets sorted → canonical form, exact ==. Two load-bearing operations:
qshift()— the summation shiftσ(n ↦ n+1, i.e.x ↦ q·xin every theta argument), the elliptic analogue ofQPoly.qshift; what the elliptic-Gosper antidifference / elliptic-Zeilberger telescoping (rc61/rc62) will consume.is_elliptic()— the balancing / very-well-poised predicate, defined operationally: a theta-hypergeometric term-ratio is elliptic (a genuine function on the elliptic curveℂ*/⟨p⟩) iff invariant under the period shiftx ↦ p·x— sois_elliptic() ≡ (pshift() == self). The exact theta-canonicalization decides it; no hand-rolled balancing algebra. This is the structural gate the elliptic reducers consult before attempting a closed form; an unbalanced input is honestly out of the row.
Plus a bug-fix to the rc59 Theta.canonicalize sign: Q(-1,1) ** k raised on a NEGATIVE odd k (which EllRatio.pshift introduces via p⁻¹) — replaced with Q(-1,1) if k odd ((−1)^k needs no exponentiation; the rc59-published copy has the latent bug but no rc59 caller produces negative p-exponents). CARRIER (peer of EllMonomial/Theta) — no ToolEntry, no new MCP param type: tools.total stays 335, ABI 3 (Rosetta-completeness + tool-schema-coverage pass Python-only; C peer owed). Verified: θ(ax)θ(bx)/[θ(x)θ(abx)] is elliptic, θ(ax)/θ(x) is not (pshift = a⁻¹·itself); the eval cross-check confirms an elliptic ratio is exactly pshift-invariant. Foundation for rc61 elliptic_gosper.
[0.9.0rc59] - 2026-06-24¶
srmech.amsc.ellbase — the EXACT theta-factor FOUNDATION of the ELLIPTIC F929 reduction row (rc59 of the post-§76 elliptic arc; the top of the base-axis degeneration tower elliptic → q → ordinary). Where Poly carries the ordinary ground (q → 1) and QPoly the basic/q ground (p → 0), the elliptic level — Frenkel–Turaev's ₁₀E₉; Warnaar, Constr. Approx. 18 (2002) 479–502 — carries term-ratios built from the modified theta function θ(z; p) = ∏_{j≥0}(1 − z·pʲ)(1 − z⁻¹·p^{j+1}), a doubly-periodic (elliptic) object: the un-collapsed top fiber, a genuine torus / "cycle of cycles". Two exact, numpy-free, float-free carrier atoms:
EllMonomial— a signed Laurent monomialcoeff · ∏ sym**eover the multiplicative argument-lattice (symbolsq, the nomep, and parameters);coeffan exactQ(sign = Class-K, neverabs()), every exponent an int. Exact*/inv/**/eval.Theta— the symbolθ(z; p)for anEllMonomialargumentz.Theta.canonicalize()applies the theta quasi-periodicity + inversion rewrites — empirically pinned in exact ℚ at build (θ(pᵏ·z₀) = (−1)ᵏ·p^{−k(k−1)/2}·z₀⁻ᵏ·θ(z₀),θ(z⁻¹) = −z⁻¹·θ(z)) — to reduce the argument to a canonicalp-exponent-0, orientation-fixed representative, emitting the exactEllMonomialprefactor (value-preserving:prefactor · θ(z₀) == θ(z), verified against the exact-ℚ truncated theta product).Theta.pochhammer(a, n)is the elliptic shifted factorial(a; q, p)_n = ∏_{k<n} θ(a·qᵏ; p)— the building block of the very-well-poised ₁₀E₉.
CARRIER (peer of Poly/QPoly/QBiPoly/TriPoly) — no ToolEntry, no new MCP param type: tools.total stays 335, ABI 3. The 1:1 C peer is owed by the everything-mirrors discipline (the rc59-prefix backlog, like TriPoly). The exact-rational truncated modified-theta product is also how the quasi-periodicity sign conventions were verified (no math, no numpy, no abs()). This is the foundation for rc60 EllRatio → rc61 elliptic_gosper → rc62 elliptic_zeilberger → rc63 elliptic_wz_certificate (the elliptic engine mirroring the §76 telescope trio one algebra up; keystone = the Frenkel–Turaev ₁₀E₉ sum).
[0.9.0rc58] - 2026-06-24¶
The F929 dispatch.infer router learns the two post-§76 Σ sub-rows — the multivariate "sums of sums" and q-hypergeometric reduction rows now AUTO-JOIN the dispatch table. srmech.amsc.dispatch.infer (the OPEN/infer meta-dispatcher that detects which reduction-theory row a stored relationship matches, tries the matching reducer, verifies the reducer's OWN check, and returns the closed form — else honest OPEN) gains two new sub-rows beside the existing cyclic / spectral / Σ:
row="sigma_multivar"— the six(n,j,k)TriPoly term-ratios (rn_*/rj_*/rk_*) of a "sums of sums"Σ_{j,k} F(n,j,k)route toapagodu_zeilberger(rc53); a non-None minimal-order annihilating recurrence is the verification. Therj_*pair makes this strictly more specific than the ordinary Σ row, so it is detected first.row="sigma_q"— a definite q-sum's four QBiPoly q-term-ratios (qrn_*/qrk_*) route toq_wz_certificate(rc57; accepted ONLY on its ownverifiedflag); an indefinite QPoly q-term-ratio (q_term_ratio_*) routes toq_gosper(rc55).
Both the structural sniff and the explicit row= / kind= tag paths recognise the new rows; the _OPEN_HINTS candidate-next-theory now points past them (multibasic / elliptic-hypergeometric ₁₀E₉ for q, higher-arity TriPoly⁺ for multivar). Pure orchestration over the already-C-mirrored reducers — non_compute, no new C peer, no new carrier, no new ToolEntry: tools.total stays 335, ABI 3. The anti-hallucination contract is unchanged — infer NEVER returns reducible: True for a reduction it did not verify. Tests: a real Σ_{j,k} C(n,j)C(j,k)=3ⁿ routes+verifies via apagodu_zeilberger; a genuine q-WZ pair via q_wz_certificate; Σ qᵏ via q_gosper; and a non-q-WZ payload returns honest OPEN. With this, all six F929 reduction-row ops (rc52–57) are reachable through the one infer dispatch table.
[0.9.0rc57] - 2026-06-24¶
q_wz_certificate — the q-Wilf–Zeilberger pair method; the identity-PROOF op; the THIRD and FINAL op of the q-hypergeometric F929 reduction row — CLOSES the row AND the whole multivariate + q-hypergeometric reduction-theory arc. For a proper q-hypergeometric term F(n,k) given by its bivariate-q ratios (QBiPoly over (X,Y)=(qⁿ,qᵏ)), q_wz_certificate(rn_num,rn_den, rk_num,rk_den) PRODUCES + VERIFIES the q-WZ certificate R(X,Y) whose companion G = R·F makes the q-WZ equation r_n(X,Y) − 1 = R(X,qY)·r_k(X,Y) − R(X,Y) an exact identity → {certificate, verified: True}, else None.
FIND reuses the rc56 q-Zeilberger order-1 path (the forced f(n+1)−f(n)=0, [−1,+1], certificate, rescaled by 1/a₁). VERIFY is the new degree-bounded primitive: the exact bivariate-ℚ[q] identity (An−Ad)·(σ_y(Xd)·Bd·Xd) == (σ_y(Xn)·Bn·Xd − Xn·σ_y(Xd)·Bd)·Ad (σ_y:Y↦qY) — no solve, no order bound. Because the check is degree-bounded not order-bounded, its C peer srmech_q_wz_verify is a COMPLETE C mirror (contrast rc42/rc56's order-≤1-only native path): a full exact-ℚ[q] QBiPoly toolkit over caller-arena bignum, malloc-free, JPL-clean, OVERFLOW-not-wrap, all locals {0}-init, per-dimension arena sizing (Y/X/q sized independently → no cubic RAM blow-up; the bounded fiber per variable). Exact over ℚ(q); numpy/math/abs()-free. ToolEntry (c_dispatched), has_native_q_wz_verify(), tools.total 334 → 335, ABI 3.
Verified: the q-WZ equation holds for known pairs by independent exact bivariate-ℚ(q) evaluation at several (q,n,k) (the constant-summand R=0 pair end-to-end, two constructed nontrivial triples incl. R=Y/(X−Y)); a wrong certificate → not verified; a non-WZ term → None; native==pure byte-identical.
The q-row is complete: gosper(rc41)/zeilberger(rc42)/wz_certificate(rc43) ordinary → q_gosper(rc55)/q_zeilberger(rc56)/q_wz_certificate(rc57) q-analog, each built on the last (QPoly rc54 → QBiPoly rc56 carriers). Cites Koornwinder, J. Comput. Appl. Math. 48 (1993) 91–111 (q-WZ method); Wilf & Zeilberger, Invent. Math. 108 (1992) 575–633 (the WZ pair).
[0.9.0rc56] - 2026-06-24¶
q_zeilberger — the q-analog of Zeilberger creative telescoping; the recurrence-finder for DEFINITE q-hypergeometric sums; the SECOND op of srmech's q-hypergeometric F929 reduction row. For f(n) = Σ_k F(n,k) with bivariate-q term ratios F(n+1,k)/F(n,k) = rn(qⁿ,qᵏ) and F(n,k+1)/F(n,k) = rk(qⁿ,qᵏ), returns the minimal-order linear q-recurrence {order, coeffs:[QPoly in qⁿ], certificate} (Σ_j a_j(qⁿ)·f(n+j) = 0, with the q-Gosper certificate) when one exists ≤ max_order, else None.
Introduces the QBiPoly bivariate-q carrier (the q-analog of BiPoly): "poly in Y=qᵏ with QPoly-in-X=qⁿ coefficients" over ℚ[q], shipping the two q-shifts σ_x (rides QPoly.qshift) and σ_y — internal to the q-row (not a ToolEntry, same as BiPoly/QPoly). q-Zeilberger PARAMETRIZES the rc55 q-Gosper-in-k solve with the unknown a_j(X) coefficients (reusing q_gosper._Cq ℚ(q) field + the exact Gauss-Jordan), cleared by ONE shared ℚ(q) denominator so the recurrence↔certificate scaling is preserved (load-bearing for the q-WZ relation rc57 will verify). Exact over ℚ(q); numpy-free, math-free, no abs(). 1:1 C peer srmech_q_zeilberger (caller-arena bignum, malloc-free, JPL-clean, OVERFLOW-not-wrap, all locals {0}-init; completes the canonical k-free order-1 q-geometric case natively, has=0 re-decides the COMPLETE pure path → never a false "no recurrence"). ToolEntry (c_dispatched), has_native_q_zeilberger(), tools.total 333 → 334, ABI 3.
Verified: the found order-1 recurrence ANNIHILATES the q-binomial-theorem sum Σ_k [n,k]_q q^{C(k,2)} = ∏_{i<n}(1+qⁱ) exact over ℚ ((1+qⁿ)f(n) − f(n+1) = 0); the certificate q-WZ relation Σ_j a_j F(n+j,k) = Δ_q(R·F) holds exactly; max_order too low → None; native==pure byte-identical. Second op of the q-row (rc57 q_wz closes it). Cites Koornwinder, J. Comput. Appl. Math. 48 (1993) 91–111.
[0.9.0rc55] - 2026-06-24¶
q_gosper — the q-analog of Gosper indefinite hypergeometric summation; the FIRST ToolEntry op of srmech's q-hypergeometric F929 reduction row. Given the q-term ratio t_{k+1}/t_k = r(qᵏ) = num/den (two Laurent-in-x=qᵏ exact-ℚ[q] QPoly), returns the rational q-antidifference certificate R(x) ({num, den} QPoly, with T(k)=R(qᵏ)·t(k) and Δ_q T = t) when the term is q-Gosper-summable, else honest None (the un-summable residue IS the no-hallucination discipline).
Pipeline (the q-analog of rc41 gosper): reduce r over ℚ(q)[x] → q-Gosper–Petkovšek normal form r = (a/b)·(c(qx)/c(x)) with gcd(a(x), b(qʲx)) = 1 ∀j≥0 peeled at the q-dispersion shifts → solve the q-Gosper equation by undetermined coefficients via exact Gauss-Jordan over the field ℚ(q) (each unknown yⱼ is itself a rational function in q). Exact over ℚ(q) — a local _Cq rational-function-in-q field layer, since polynomial GCD needs a field and ℚ[q] (QPoly's ground ring) is not one. numpy-free, math-free, no abs(). 1:1 C peer srmech_q_gosper (caller-arena bignum, malloc-free, JPL-clean, OVERFLOW-not-wrap; has=1 only on a real native solve, has=0 re-decides the COMPLETE pure path → never a false "no certificate"). ToolEntry (c_dispatched), has_native_q_gosper(), tools.total 332 → 333, ABI 3.
Verified: the certificate identity R(qx)·r(x) − R(x) = 1 exact over ℚ (≡ Δ_q(R·t) = t); the un-summable case returns None; native==pure byte-identical. First op of the q-row (rc56 q_zeilberger parametrizes this engine). Cites Koornwinder, J. Comput. Appl. Math. 48 (1993) 91–111.
[0.9.0rc54] - 2026-06-24¶
QPoly — exact q-shift carrier — the foundation for the q-hypergeometric F929 reduction row (q-Gosper → q-Zeilberger → q-WZ, rc55–57). The q-analog of Poly/BiPoly: a Laurent polynomial in x = qⁿ whose coefficients are exact polynomials in q (Poly-in-q cells over a signed x-exponent window — the signed x_low carries the negative x-powers the q-shift term-ratio algebra produces). The load-bearing new operations are the q-shift σ: x ↦ q·x (qshift(s)) and the q-difference Δ_q = σ − id (qdelta) — exactly what q-Gosper/q-Zeilberger consume.
Surface: from_coeffs/from_dict/zero/one/from_q_poly, exact +/−/·, eval(q,x)→Q, qshift/qdelta. Exact over ℤ/ℚ[q] (no float — ℚ[q] is the minimal sufficient ground ring; denominators in q enter only at the downstream QMat solve, never in the carrier); numpy-free, math-free, no abs(). 1:1 C peer srmech_qpoly_{add,sub,mul,qshift} + ws_bound over caller-arena bignum (malloc-free, JPL-clean, OVERFLOW-not-wrap), byte-identical to the pure path over a >2⁶⁴ keystone; has_native_qpoly() flag, hasattr-guarded dispatch. Carrier (peer of Poly/BiPoly/TriPoly) → not a ToolEntry, tools.total stays 332, ABI 3. Verified: the q-difference Δ_q T(x) == T(qx) − T(x) and the q-telescoping Σᵢ Δ_q T(x₀qⁱ) == T(x₀q^{m+1}) − T(x₀) exact over ℚ. First rc of the q-hypergeometric arc (rc55 q_gosper consumes qshift/qdelta). Cites Koornwinder, J. Comput. Appl. Math. 48 (1993) 91–111.
[0.9.0rc53] - 2026-06-23¶
apagodu_zeilberger — the multivariate "sums of sums" creative-telescoping recurrence-finder — CLOSES the multivariate F929 reduction row. The double-sum generalization of zeilberger: for f(n)=Σ_{j,k} F(n,j,k) it finds the minimal-order recurrence Σᵢ aᵢ(n)·f(n+i)=0 via the Apagodu–Zeilberger two-certificate ansatz Σᵢ aᵢ(n)·F(n+i,j,k)=Δⱼ Gⱼ + Δₖ Gₖ, cleared over the rc52 TriPoly ℚ[n,j,k] (consuming its Δⱼ/Δₖ difference operators) and solved by the rc40 QMat exact-ℚ RREF. apagodu_zeilberger(rn_num,rn_den, rj_num,rj_den, rk_num,rk_den, max_order=…) — the three TriPoly term-ratios r_n=F(n+1,j,k)/F, r_j=F(n,j+1,k)/F, r_k=F(n,j,k+1)/F — returns {order, coeffs:[Poly_in_n], certificate_j, certificate_k} or honest None.
Verified: Σⱼ Σₖ C(n,j)C(j,k)=3ⁿ → f(n+1)−3f(n)=0 (cross-checked against composing two zeilberger calls); Σ C(n,j)C(n−j,k)=3ⁿ → order-1, native==pure byte-identical; both recurrences annihilate 3ⁿ; honest None past max_order; zero-denominator rejected. Complete 1:1 C peer srmech_apagodu_zeilberger (own exact-ℚ trivariate toolkit + srmech_qmat_rref, caller-arena, JPL-clean) accelerating order ≤ 1; wide systems decline to the bounded-memory CRT pure path (dispatch trusts only has=1). tools.total 331→332 (c_dispatched); ABI 3. numpy-free, math-free, no abs(). Cites Apagodu & Zeilberger, Adv. Appl. Math. 37 (2006) 139–152.
The multivariate "sums of sums" row is CLOSED: TriPoly (rc52) → apagodu_zeilberger (rc53). The Apéry-like Σ_{j,k} C(n,j)C(n,k)C(j+k,j) ([1,5,33,245,1921,…]) is order-2 with a high-degree cross-term certificate; its annihilation test is opt-in (SRMECH_RUN_APERY=1) — the pure path proves it.
[0.9.0rc52] - 2026-06-23¶
TriPoly — exact-ℚ[n,j,k] polynomial carrier — the foundation for the multivariate "sums of sums" creative-telescoping row (Apagodu–Zeilberger). The 3-variable sibling of the rc42 BiPoly (ℚ[n,k]): a j-ascending tuple of BiPoly-in-(n,k). Carrier surface — from_coeffs/from_dict/zero/one/from_n_poly/from_bipoly, exact +/−/·, eval(n,j,k)→Q, shift_n/shift_j/shift_k, and the difference operators delta_j/delta_k (shift_x(1) − self) that express the joint certificate Σᵢ aᵢ(n)·F(n+i,j,k) = Δⱼ Gⱼ + Δₖ Gₖ the rc53 op will solve.
Exact rational over Python-int bigint (no float); numpy-free, math-free, no abs(). 1:1 C peer srmech_tripoly_{add,sub,mul} + srmech_tripoly_ws_bound over caller-arena bignum (malloc-free, JPL-clean, OVERFLOW-not-wrap), byte-identical to the pure path over 600 randomized ops + a >2⁶⁴ keystone; has_native_tripoly() flag, hasattr-guarded dispatch, pure path the complete fallback. Carrier (peer of Poly/QMat/BiPoly) → not a ToolEntry, tools.total stays 331, ABI 3. Verified: the Δⱼ/Δₖ telescoping identity Σ_{j=0}^{m} Δⱼ G = G(m+1)−G(0) exact over ℚ (both summation vars); the C(n,j)C(n,k)C(j+k,j) term-ratio pieces representable. First rc of the multivariate "sums of sums" arc (rc53 = apagodu_zeilberger consumes it). Cites Apagodu & Zeilberger, Adv. Appl. Math. 37 (2006) 139–152.
[0.9.0rc51] - 2026-06-23¶
dispatch.infer — the F929 OPEN/infer router: ONE dispatch table over the three closed-form reduction-theory rows. The meta-dispatcher that makes the_one (cyclic row), resonant_spectrum (spectral row), and telescope (Σ row: gosper/zeilberger/wz_certificate) one callable. infer(relationship) detects which row a stored relationship matches (explicit row/kind tag, else a structural sniff — Σ term-ratios → spectral graph/Laplacian → cyclic σ/period), tries the matching reducer AND verifies it actually reduced (wz_certificate's own verified is True / resonant_spectrum's force_orders Λ²==L·L contract / the_one's (1,3,7,3) partition + plane-count contract), and returns the verified closed form {reducible:True, row, reducer, closed_form, verified:True} — else an honest OPEN {reducible:False, row:None, reason, candidate_next_theory}.
The OPEN residue makes the no-magic-numbers / no-hallucination discipline executable: the router never returns reducible:True without the reducer's own verification being True (reducer-internal contract errors are caught → honest OPEN, never a false positive). This is the capstone of the F929 dispatch table — the §76 telescope arc + the spectral/cyclic rows recognized as one table of closed-form reduction theories humans already built.
Composes the existing verified reducers — no new math. Classified non_compute (it runs no new arithmetic; every computation rides an already-C-mirrored reducer — python_only_debt stays 105, c_exists_unbound 0, no srmech_infer C symbol, ABI 3). Lives at srmech.amsc.dispatch.infer (the Class-D late-binding module, one rung up from match). tools.total 330→331. numpy-free, math-free, no abs(). The OPEN-path return is plain-JSON; a successful closed_form carries the reducer's native carriers (Vec/Mat, as resonant_spectrum already does), MCP-coerced at the boundary.
[0.9.0rc50] - 2026-06-23¶
jacobi_sncndn_series_truncate — exact-ℚ Jacobi elliptic sn/cn/dn, the rotation-last sibling of sin/cos_series_truncate — + its 1:1 C peer srmech_jacobi_sncndn. jacobi_sncndn_series_truncate(num, den, m_num, m_den, num_terms) returns the three exact rational (num, den) pairs of sn/cn/dn at u = num/den, modulus m = m_num/m_den, via the power-series ODE sn'=cn·dn, cn'=−sn·dn, dn'=−m·sn·cn (sn(0)=0, cn(0)=1, dn(0)=1) integrated over exact ℚ — rotation-last: the bit-exact coefficient fiber accumulates in ℚ, with one terminal projection at u. This is the elliptic-function generalization of the Class-N trig series-truncate, and the executable demonstrator of the rotation-last Dzhanibekov / geometric-phase cascade (MFO §VII.6.24).
Verified: the exact-ℚ identities sn²+cn²=1 and dn²+m·sn²=1 hold (coefficient cancellation, all moduli); m=0 → sin/cos Maclaurin with dn≡1; m=1 → tanh/sech; float match to the AGM oracle at (u=0.7, m=0.5) (sn≈0.6243400909). The C peer srmech_jacobi_sncndn (c/src/srmech_jacobi.c) is a complete exact-bignum mirror over caller-arena srmech_bigint — malloc-free, JPL-clean, sign-branch Class-K (no abs), byte-identical to Python across 48+ cases. tools.total 329→330 (c_dispatched); rotation-last ledger CANONICAL (AVOIDABLE_VIOLATION stays 0); ABI 3; has_native_jacobi_sncndn() flag, hasattr-guarded dispatch with the pure path as the complete fallback. numpy-free, math-free, no abs(). Cites Abramowitz & Stegun §16.4.
[0.9.0rc49] - 2026-06-23¶
Fix a shipped GF(p) abstraction-bypass — route srmech_gf_rref through the Class-I srmech_mod_* primitives. The C srmech_gf_rref (rc44, the foundation of the CRT-QMat solve) carried its own private static GF(p) modular helpers — gf_add/gf_mul (raw (a∘b) % p) and gf_inv (Fermat a^(p−2)) — instead of composing the existing Class-I cyclic C primitives srmech_mod_add/srmech_mod_mul/srmech_mod_inv. Two problems: (a) it re-implemented modular arithmetic the Class-I module already owns (the Python gf_rref composes cyclic.mod_*; the C mirror did not), and (b) its Fermat inverse diverged in algorithm from the canonical extended-Euclid srmech_mod_inv (same result for prime p, but not the same code path).
rc49 rewrites the three gf_* helpers as thin wrappers over srmech_mod_add/srmech_mod_mul/srmech_mod_inv, deleting the private raw-% arithmetic and the Fermat power loop. gf_inv now uses the extended-Euclid Class-I inverse — 1:1 with Python cyclic.mod_inv. The srmech_mod_* call sits OUTSIDE the assert (survives -DNDEBUG); (void)st keeps the status used when the assert strips.
Byte-identical — verified native gf_rref == forced-pure on 60 random GF(2³¹−1) systems, and the whole QMat.rref_crt == dense rref chain (which builds on gf_rref) on 30 random + keystone Q(10⁴⁰+1, 3³⁰) matrices. JPL-clean (pedantic -Werror Release/NDEBUG compile clean; the wrappers keep ≥2 asserts each; the Fermat-loop removal only reduces complexity). No new symbols, ABI stays 3, describe()["tools"]["total"] stays 329. (Found via the HAL/PAL abstraction-layer audit: the whole rcN-run C surface uses the SIMD-HAL + platform-PAL correctly; this was the one inner-arithmetic primitive composed privately rather than through its owning Class-I module.)
[0.9.0rc48] - 2026-06-23¶
CRT-QMat re-fibration arc, rung 5 — CLOSER: srmech_qmat_rref_crt, the single-symbol C orchestration. Discharges the owed everything-mirrors backlog: a bare-C host (no Python) now calls one function for the whole bounded-memory exact-ℚ CRT solve. rc44–rc47 shipped the four underlying C rungs (srmech_gf_rref, srmech_crt_combine, srmech_rational_reconstruct, srmech_next_prime) + routed the consumers; rc48 composes them into one malloc-free, caller-arena, JPL-clean symbol.
srmech_qmat_rref_crt(new C symbol,c/src/srmech_qmat_crt.c, same wire shape assrmech_qmat_rref) orchestrates the full CRT solve: descending odd-prime walk from 2³¹ (matching Python's_gf_primes), per-primesrmech_gf_rrefover GF(p) (skip a prime dividing any denominator), unlucky-prime rank-consensus (max(rank, pivots)dominates; a strictly-higher-rank prime restarts the CRT), per-cellsrmech_crt_combine→srmech_rational_reconstruct(Wang bound), and stabilization early-termination.QMat.rref_crtnow dispatches to it (hasattr-guardedhas_native_qmat_rref_crt; the pure path stays the complete fallback).- THE ARENA BOUND (the crux). The caller-arena is sized from the answer-Hadamard good-prime budget, never the dense intermediate-swell envelope. Each per-prime solve is int64 (
n_rows·n_cols·8bytes); the only bignum is the final per-cell combine+reconstruct, bounded by the good-prime countn_primes ≤ ⌈2·log₂H/30⌉(H bounds the answer-entry magnitude, derived from the input magnitudes — NOT the elimination swell). Helperssrmech_qmat_rref_crt_ws_bound+srmech_qmat_rref_crt_entry_cap. Measured on Franel 484×154: CRT arena 170 MB vs the densesrmech_qmat_ws_bound2.31 GB (13.6:1) — answer-sized, sub-GB. (A pathological huge answer hits the budget →SRMECH_ERR_OVERFLOW→ Python falls back to its ceiling-free pure CRT path.)
Verified native == pure == dense byte-identical: 40+ random shapes (square / non-square / rank-deficient), keystone Q(10⁴⁰+1, 3³⁰) (>2⁶⁴ answer entries), first-prime-unlucky restart, and the real Franel 484×154 system (heavy, gated). JPL Power-of-Ten clean (≤60-line functions, ≥2 asserts/fn, no goto/malloc/abs; pedantic -Werror Release/NDEBUG compile clean across all C sources); a bare-C smoke c/test/test_srmech_qmat_crt.c proves a no-Python host computes the exact-ℚ RREF. ABI stays 3 (additive symbol, hasattr-guarded), describe()["tools"]["total"] stays 329 (rref_crt is a carrier method, not a ToolEntry — the C peer doesn't change the count), Rosetta unchanged (carrier methods carry no ledger row). numpy-free, math-free.
The CRT-QMat re-fibration arc (rc44→rc48) is COMPLETE: srmech's exact-ℚ linear algebra now solves in the bounded CRT fiber (Class I modular ∘ J primes ∘ N rational reconstruction) end-to-end — Python and a bare-C host alike — instead of reserving the GB-scale dense Hadamard envelope. The red-flag rule (working-RAM explosion = a missed fiber, never dense) made executable.
[0.9.0rc47] - 2026-06-23¶
CRT-QMat re-fibration arc, rung 4 (THE PAYOFF) — route the exact-ℚ consumers through the CRT fiber. rc44–rc46 built the bounded-memory exact-ℚ solver (gf_rref → crt_combine → rational_reconstruct → QMat.rref_crt); rc47 makes the consumers USE it, so the dense Hadamard-envelope arena is gone where it mattered.
zeilbergerundetermined-coefficient solve routed through CRT. The Zeilberger creative-telescoping kernel (the consumer that hit the dense ~5 GB Franel wall) now reduces its homogeneous system throughQMat.rref_crtonce the system is large enough to benefit (auto-by-size: cell count > 4096 → bounded CRT; tiny systems stay on the faster denserref). Byte-identical recurrence + certificate either path — proven by forcing both paths on the rc42 cases (Σ C(n,k)=2ⁿ,Σ C(n,k)²=C(2n,n)) and asserting equality. The original rc44 dense order-≥2 Zeilberger backfill is now fully DISSOLVED: high-order/high-degree definite sums solve at bounded memory through the fiber — there is no dense-arena cap to work around on the Python path.- Demonstrated on
Σ_k C(n,k)⁴(OEIS A005260, minimal recurrence order 2): the degree-4 ratios make this a larger undetermined-coefficient system than the degree-3 order-2 Franel one, solved at bounded memory via the CRT-routed kernel and cross-checked by direct concrete summation (Σ_j a_j(n)·f(n+j) = 0). Gated behindSRMECH_RUN_HEAVY=1(minutes-scale pure-Python assembly). QMat.det/inverse/solve/nullspace/rankgain amethod="auto"|"dense"|"crt"kwarg."auto"(default) dispatches by size — CRT once the cell count exceeds the same 4096 threshold (where the dense Hadamard arena would balloon), dense below it (cheaper, faster)."crt"/"dense"force the path explicitly. All three give the byte-identical exact-ℚ result (verified dense == crt on random matrices, keystoneQ(10⁴⁰+1, 3³⁰)magnitudes > 2⁶⁴, and a rank-deficient nullspace);"crt"runs at bounded memory.
Surface: routing + a method= kwarg on existing QMat carrier methods only — no new ToolEntry (describe()["tools"]["total"] stays 329), Rosetta unchanged, ABI stays 3. numpy-free, math-free, no abs() (Class-K). rref_crt is composition_of_c, so the routing needs no new C; the single-symbol srmech_qmat_rref_crt C orchestration remains the owed everything-mirrors backfill (rc48).
[0.9.0rc46] - 2026-06-23¶
CRT-QMat re-fibration arc, rung 3 — QMat.rref_crt: the exact-ℚ RREF at bounded memory. The fiber assembles. QMat.rref_crt() is a new QMat carrier method that computes the same exact-rational RREF as the dense QMat.rref() ({rref, rank, pivots}, byte-identical) — but via CRT instead of the dense Gauss-Jordan whose malloc-free arena reserves the Hadamard worst-case intermediate-fraction envelope. It composes the rc44+rc45 pieces: descending machine-int primes (< 2³¹, walking primes.is_prime) → gf_rref over GF(p) per prime (swell-free) → unlucky-prime rank-consensus (the max-rank key dominates; lower-rank primes discarded, the p=7 Franel mechanism — and a strictly-higher rank later restarts the CRT) → crt_combine per cell → rational_reconstruct → stabilization early-termination (return when the reconstructed rational matrix is identical across two consecutive good primes).
The headline (order-2 Franel 484×154 system): the dense exact-ℚ path reserves a ~2.3 GB Hadamard-envelope arena (faithful eval of the srmech_qmat_ws_bound formula); the CRT path's per-prime int64 matrix is 0.596 MB (a ~3867:1 footprint reduction) and its full Python peak working set is ~19 MB, using 3 primes. Same exact RREF, bounded memory — the fiber, not the dense reservation.
Surface: rref_crt is a QMat carrier method (like rref/det/inverse), NOT a ToolEntry → describe()["tools"]["total"] stays 329; Rosetta classification composition_of_c (it composes the C-backed gf_rref/crt_combine/rational_reconstruct/next_prime — all attested native==pure in rc44/rc45); ABI stays 3. numpy-free, math-free, no abs() (Class-K). The single-symbol srmech_qmat_rref_crt C orchestration (a bare-C host calling ONE function) is the owed everything-mirrors backfill — the stabilization loop over a growing CRT product needs careful malloc-free arena bounds, deferred to keep this rc focused (the four underlying ops are already C-backed).
Verified byte-identical rref_crt == rref on 60 random shapes (incl. rank-deficient), keystone-magnitude entries (Q(10⁴⁰+1, 3³⁰), num/den > 2⁶⁴), first-prime-unlucky consensus-restart, and the real order-2 Franel 484×154 system.
[0.9.0rc45] - 2026-06-23¶
CRT-QMat re-fibration arc, rung 2 — crt_combine + rational_reconstruct: the "project once at the end" closers. Rung 1 (rc44) gave gf_rref (bounded GF(p) field-RREF) + next_prime. This rc adds the two ops that turn a set of per-prime modular residues back into the exact rational answer — completing the I∘J∘N recipe (modular fibers ∘ primes ∘ rational reconstruction):
crt_combine(residues, moduli)(srmech.amsc.modular_linalg, Class I) — iterative Garner CRT: given residues and pairwise-coprime moduli (the distinct CRT primes), returns{"residue", "modulus"}withresidue ≡ rᵢ (mod mᵢ)andmodulus = ∏ mᵢ. The combined modulus exceeds 64 bits, so this is bignum (Pythonint, no ceiling).rational_reconstruct(residue, modulus, *, num_bound=None, den_bound=None)(srmech.amsc.rational, Class N) — Wang half-GCD extended-Euclidean reconstruction: recovers the uniquep/q ≡ residue (mod modulus)within the bounds (default symmetric Wang boundisqrt(modulus // 2)), orNoneif none exists. Sign is Class-K (neverabs()).
Both ship malloc-free, JPL-clean C peers over the caller-arena srmech_bigint (c/src/srmech_crt_reconstruct.c: srmech_crt_combine + srmech_rational_reconstruct), wired via _native.py (has_native_crt_combine / has_native_rational_reconstruct); native == pure byte-identical including moduli > 2⁶⁴. Public ToolEntries → describe()["tools"]["total"] 327 → 329, both Rosetta c_dispatched; ABI stays 3.
Verified: crt_combine vs an independent CRT oracle (>2⁶⁴ moduli exercised); rational_reconstruct round-trips (p·q⁻¹ mod M) → (p,q) vs Fraction + honest None-out-of-bound; and the I∘J∘N mini end-to-end — gf_rref over several primes → crt_combine → rational_reconstruct recovers the fractions.Fraction dense-RREF answer byte-for-byte (with unlucky-prime skipping), confirming the fiber composes to the exact rational at bounded memory. C smoke 25/25 under both Release (-DNDEBUG) and debug pedantic -Werror; source pure-ASCII.
rc46 next assembles gf_rref + these into the full QMat CRT solve (byte-identical to dense QMat.rref, ~369:1 arena collapse); rc47 routes det/inverse/solve/nullspace through it and removes ZB_MAX_ORDER.
[0.9.0rc44] - 2026-06-23¶
CRT-QMat re-fibration arc, rung 1 — gf_rref + next_prime: the swell-free modular foundation. This opens the arc that re-fibrates srmech's exact-ℚ linear algebra off the dense Gauss-Jordan RREF (whose malloc-free caller-arena reserves the Hadamard worst-case intermediate-fraction envelope — measured 1.54 GB for a 17-bit answer on a 484×154 exact-ℚ system, a 369:1 over-reservation) and onto CRT (Class I modular fibers ∘ Class J primes ∘ Class N rational reconstruction), where each per-prime solve is bounded and swell-free. Rung 1 ships the bottom of that stack:
gf_rref(rows, p)(srmech.amsc.modular_linalg, Class I) — reduced row-echelon form of an integer matrix over the field GF(p) (2 < p < 2³¹, soa·bfits one uint64). Returns{"rref", "rank", "pivots"}with entries in[0, p). Pure-Python path composescyclic.mod_inv/mod_mul/mod_add; sign/zero handling is Class-K (compare-to-0, neverabs()). The whole point: a fixed-size machine-int matrix, no bignum, no intermediate swell — the order-2 Franel 484×154 system runs in a 0.596 MB int64 matrix vs the dense ℚ path's ~1.5 GB arena (~2500× smaller).next_prime(n)(srmech.amsc.primes, Class J) — the prime successor for the CRT prime sequence.
Both ship with malloc-free, JPL-clean C peers (srmech_gf_rref in c/src/srmech_modular_linalg.c; srmech_next_prime in c/src/srmech_primes.c), wired via _native.py (has_native_gf_rref / has_native_next_prime); native == pure byte-identical. Public ToolEntries → describe()["tools"]["total"] 325 → 327, both Rosetta c_dispatched; ABI stays 3 (additive symbols). Verified: gf_rref byte-identical to an independent GF(p) field-elimination oracle on 2240 random matrices (7 shapes × 7 primes, incl. rank-deficient) AND the live Franel order-2 Zeilberger system (rank + entries + pivots, incl. the genuine unlucky-prime rank drop at p=7); C smoke passes under both Release (-DNDEBUG) and debug pedantic -Werror; source pure-ASCII.
The arc continues: rc45 = crt_combine + rational_reconstruct → rc46 = the QMat CRT solve (byte-identical to dense, ~369:1 arena collapse) → rc47 = route det/inverse/solve/nullspace + re-aim Zeilberger + remove ZB_MAX_ORDER (order ≥ ⅔ at bounded memory).
[0.9.0rc43] - 2026-06-23¶
§76 TELESCOPE CLOSED — wz_certificate: the Wilf–Zeilberger pair method, the PROOF op that completes the Σ-row prover. The §76 telescope is now the full closed-form pipeline: gosper (indefinite summation) → zeilberger (definite-sum recurrence) → wz_certificate (identity proof) (the F929 dispatch-table Σ-row). Given a proper hypergeometric term F(n,k) (the normalized summand of a terminating identity Σ_k F(n,k) = const) by its two term ratios, wz_certificate(rn_num, rn_den, rk_num, rk_den) (1) FINDS the WZ certificate R(n,k) (a rational function, BiPoly num/den) whose companion G=R·F satisfies the WZ equation F(n+1,k)−F(n,k) = G(n,k+1)−G(n,k), and (2) VERIFIES that equation as an exact bivariate-ℚ rational-function identity — returning {"certificate": {"num": BiPoly, "den": BiPoly}, "verified": True} or None. FIND reuses the rc42 zeilberger machinery (the WZ certificate is the order-1 forced-recurrence Zeilberger certificate); VERIFY is the new primitive.
Ships with the malloc-free C peer srmech_wz_verify (c/src/srmech_wz.c) — and the verification is a COMPLETE C mirror: the WZ-equation identity check is bounded only by input degree, NOT by any order (contrast rc42's order-≤1 peer), so the full verifier runs in caller-arena C (composing the bivariate-ℚ poly toolkit; JPL-clean, no abs()). wz_certificate routes through it when HAS_NATIVE (has_native_wz_verify()); pure-Python the complete alternative. Public ToolEntry → describe()["tools"]["total"] 324 → 325, Rosetta c_dispatched; ABI stays 3. (Also fixed a pre-existing return-type-honesty harness gap: a decision op's None return now verifies against a dict | None union instead of false-failing on the sibling dict arm.)
Verified: native↔pure byte-identical; WZ pairs cross-checked by independent evaluation of the WZ equation AND sum-constancy — Σ_k C(n,k)=2ⁿ with the classic certificate R = −k/(2(n+1−k)); Σ_k C(n,k)²=C(2n,n) (Wilf–Zeilberger 1990); Σ_k C(n,k)·2ᵏ=3ⁿ. C smoke 10/10 under BOTH Release (-DNDEBUG) and debug pedantic -Werror; source pure-ASCII.
[0.9.0rc42] - 2026-06-23¶
§76 TELESCOPE — zeilberger: Zeilberger's creative telescoping, the recurrence-finder for DEFINITE hypergeometric sums (the Σ-row prover's second op). Given a proper hypergeometric term F(n,k) by its two term ratios r_n = F(n+1,k)/F(n,k) and r_k = F(n,k+1)/F(n,k) (each a numerator/denominator pair of the new exact-ℚ[n,k] BiPoly bivariate carrier), zeilberger(rn_num, rn_den, rk_num, rk_den, max_order=6) returns the minimal-order linear recurrence Σ_{j=0}^{L} a_j(n)·f(n+j) = 0 satisfied by f(n)=Σ_k F(n,k) — {"order": L, "coeffs": [Poly_in_n, ...], "certificate": BiPoly} — or None. Method = parametrized creative telescoping: for each order L it forms the homogeneous exact-ℚ system in {a_j(n) coeffs} ∪ {WZ-certificate coeffs} and reads a nonzero-a kernel via the rc40/rc41 QMat/gosper machinery. New carrier BiPoly (polynomial in k with Poly-in-n coefficients; no float/abs()/math/numpy).
Ships with the malloc-free C peer srmech_zeilberger (c/src/srmech_zeilberger.c, a compact exact-ℚ bivariate-poly toolkit over caller-arena srmech_bigint composing srmech_qmat_rref; JPL-clean, no abs()). C scope: the peer accelerates order ≤ 1 (covers the canonical order-1 identities in a bounded arena); higher orders / overflow use the complete pure-Python path — the full-order C mirror is owed everything-mirrors backlog. The dispatch trusts only a positive (has=1) C result; a has=0 always re-decides in pure Python, so C never returns a false "no recurrence." zeilberger is a public ToolEntry → describe()["tools"]["total"] 323 → 324, Rosetta c_dispatched; ABI stays 3.
Verified: native↔pure byte-identical on every accelerated case; recurrences cross-checked by independent direct summation — Σ_k C(n,k)=2ⁿ → order-1 f(n+1)−2f(n)=0; Σ_k C(n,k)²=C(2n,n) → order-1 (n+1)f(n+1)−(4n+2)f(n)=0. C smoke 8/8 under BOTH Release (-DNDEBUG) and debug pedantic -Werror; source pure-ASCII. Closes with rc43 wz_certificate (the WZ-pair verifier) to complete the Σ row.
[0.9.0rc41] - 2026-06-22¶
§76 TELESCOPE — gosper: Gosper's indefinite hypergeometric summation, the FIRST op of the Σ-row closed-form prover (F929 dispatch-table). Given a hypergeometric term by its ratio t(k+1)/t(k) = num(k)/den(k) (two exact-ℚ Poly), gosper(num, den) returns the antidifference certificate R(k) (a rational function {"num": Poly, "den": Poly} with T(k)=R(k)·t(k), T(k+1)−T(k)=t(k)) or None when Σ t(k) has no hypergeometric closed form. Composes the rc38/rc39 Poly toolkit (gcd/shift/divmod/dispersion) for the Gosper–Petkovšek normal form + the rc40 exact-ℚ matrix solve (srmech_qmat_rref) for the undetermined-coefficient Gosper equation. Ships CO-EQUAL with the malloc-free C peer srmech_gosper (c/src/srmech_gosper.c, orchestrating srmech_poly_* + srmech_poly_gcd + srmech_qmat_rref; caller-arena, JPL-clean, no abs()); gosper routes through it when HAS_NATIVE (has_native_gosper()), pure-Python the complete alternative. gosper is a public ToolEntry → describe()["tools"]["total"] 322 → 323, Rosetta c_dispatched; ABI stays 3 (additive symbols). Verified: byte-identical Python(pure) == C across a 53-case stress sweep + concrete-summation acceptance — Σ_{0}^{n-1} k = n(n−1)/2, Σ_{1}^{n} k·k! = (n+1)!−1, harmonic 1/k → None, 1/(k(k+1)) telescopes to −1/k; C smoke 12/12 pedantic -Werror. Unblocks rc42 zeilberger + rc43 wz_certificate (the rest of the Σ row).
[0.9.0rc40] - 2026-06-22¶
C-MIRROR BACKFILL (everything-mirrors) — srmech_qmat_*: the malloc-free C peer of QMat's exact-ℚ linear algebra. Discharges the owed QMat C-mirror backlog item AND is the prerequisite for §76 gosper (whose undetermined-coefficient step needs an exact-ℚ linear solve in C). Six new C symbols in c/src/srmech_qmat.c mirroring QMat's compute surface: srmech_qmat_rref (canonical reduced row echelon over ℚ — the shared kernel), srmech_qmat_rank, srmech_qmat_det, srmech_qmat_inverse, srmech_qmat_solve (the Gosper prerequisite), srmech_qmat_nullspace + sizers srmech_qmat_ws_bound/srmech_qmat_entry_cap. Bignum-ℚ entries (two srmech_bigint num/den), caller-arena (no malloc), JPL-clean (helpers ≤60 lines, ≥2 asserts, Class-K int ±1 swap-sign — no abs()); bound in _native.py (has_native_qmat()), QMat.{rref,rank,det,inverse,solve,nullspace} route through them when HAS_NATIVE with pure-Python the complete ceiling-free alternative.
QMat is a carrier (bignum_reference bucket) → describe()["tools"]["total"] stays 322 (no ToolEntry, no Rosetta row, no count-test change); ABI stays 3 (additive symbols, bound via hasattr). Arena soundness: every reduced RREF entry is a ratio of input minors (Cramer), Hadamard-bounded; qmat_cap_for sizes each carrier to dominate that minor bit-length, with per-op gcd-reduction keeping intermediates reduced; residual overflow → SRMECH_ERR_OVERFLOW (never a silent wrap) → pure fallback. Verified: byte/value-identical Python(pure) == C == an independent fractions.Fraction cofactor oracle on the rc34 QMat keystone (Q(10^k, p)-class entries → multi-tens-of-digits det numerator > 2⁶⁴), full-rank (A·A⁻¹==I, A·solve(b)==b) and singular (det==0, no inverse, element-for-element nullspace with A·v==0, rank-nullity) cases; the coefficient-growth ceiling (no spurious overflow); standalone test_srmech_qmat.c smoke 9/9 pedantic -Werror; WSL native parity 64/64 (assertions ran, not skipped); the full existing QMat/triality/eigen suite (521 passed) — no consumer regressed; full ratchet/JPL/Rosetta/coverage gate green. Tests in test_qmat_c_rc40.py. 5-SSOT 0.9.0rc39 → 0.9.0rc40.
[0.9.0rc39] - 2026-06-22¶
FOUNDATION (PR #687 §76 arc, rc39 — the Gosper polynomial-algebra layer) — srmech_poly_gcd C peer + Poly.resultant + Poly.dispersion. The next layer toward the Σ-row prover, built on the rc38 Poly carrier. Three additions, all Poly carrier methods (NOT ToolEntries → describe()["tools"]["total"] stays 322; classified like Poly/QMat, no Rosetta row):
- srmech_poly_gcd C (the item deferred from rc38) + srmech_poly_gcd_ws_bound; Poly.gcd routes through it when HAS_NATIVE (has_native_poly_gcd()). The arena fix: per-step monic normalization tames the Euclidean intermediate-coefficient explosion (empirically worst chain/input bit-growth ~92× → ~23× over 4000 trials), and poly_gcd_cap_for sizes each carrier with degree-squared headroom that provably dominates that growth; any residual overflow returns SRMECH_ERR_OVERFLOW (never a silent wrap) and Python falls back to its ceiling-free pure-bigint Euclid. Proven on the case the naive rc38 envelope overflowed: direct C with the naive bound returns SRMECH_ERR_OVERFLOW, the chain-scaled bound succeeds (byte/value-identical to Python).
- Poly.resultant(other) -> Q — exact via the subresultant polynomial-remainder sequence (poly arithmetic only — NO matrix/determinant; rides srmech_poly_divmod).
- Poly.dispersion(other) -> sorted list[int] — the standard PWZ convention { h ≥ 0 : deg gcd(a(k), b(k+h)) ≥ 1 } (the set Gosper's Gosper–Petkovšek normal form consumes), via the integer roots of Res_k(a(k), b(k+h)) (resultant + exact rational-root sieve).
numpy-free, math-free, no abs(); caller-arena, JPL-clean (srmech_poly_gcd factored <60 lines via poly_gcd_euclid); ABI stays 3 (additive C symbols). Verified: srmech_poly_gcd byte/value-identical to Python on the rc38 bignum keystone (50-digit coeffs) + the deg-4 rational overflow-regime + 40 random shared-factor chains (WSL native, 60 parity assertions, 0 skips); resultant matches the product-of-roots definition lead(p)^deg(q)·∏ q(roots of p) (e.g. Res(x²−1,x−2)=3, exact-ℚ Res(x²+½,x−⅓)=11/18); dispersion self-consistent ((x(x+5),x)→[0,5], (x,x)→[0], no-overlap→[]); standalone test_srmech_poly.c smoke 11/11 pedantic -Werror; full ratchet/JPL/Rosetta/coverage gate green. Tests in test_poly_rc39.py. gosper itself is deferred to rc40 — as a public compute op it needs a srmech_gosper C peer (the Rosetta ratchet forbids a Python-only compute op), and that peer needs an exact-ℚ linear solve in malloc-free C (≈ the owed QMat-C backfill). 5-SSOT 0.9.0rc38 → 0.9.0rc39.
[0.9.0rc38] - 2026-06-22¶
FOUNDATION (PR #687 §76 arc, rc38 of 4) — Poly: an exact-rational polynomial-over-ℚ carrier + its srmech_poly_* C peer. The keystone for the §76 "telescope" arc (the Σ-row closed-form prover — gosper/zeilberger/wz_certificate build on this in rc39–rc41). srmech.amsc.Poly is an exact univariate polynomial whose coefficients are Q (the rational bigint — no float, no ceiling), a 1-D sibling of QMat. API: from_coeffs/from_ints/zero/one/monomial construction (ascending-degree, canonically trimmed); .degree/.leading/.is_zero/.coeffs/len/[i]; +/unary--/-, scalar *, polynomial * (convolution), ==/repr; divmod (//,%) long-division over ℚ (a == q·b + r, deg r < deg b); monic Euclidean gcd; eval (Horner → exact Q); shift (p(x) ↦ p(x+h) — the dispersion primitive the prover needs); derivative; and to_floats (the single explicit float-boundary collapse). numpy-free, math-free, no abs() (Class-K sign-branch). Poly is a carrier class, not a ToolEntry — describe()["tools"]["total"] stays 322; classified like QMat (no Rosetta row).
1:1 C peer (the everything-mirrors discipline): srmech_poly_{add,sub,mul,divmod,eval,shift} + srmech_poly_ws_bound (c/src/srmech_poly.c) over bignum-rational coefficients, caller-arena (no malloc), JPL-clean; bound in _native.py (has_native_poly()), Poly routes through them when HAS_NATIVE with pure-Python the complete alternative; ABI stays 3. srmech_poly_gcd (C) is deferred to the rc39-prefix — the Euclidean ℚ-GCD has the classic intermediate-coefficient explosion, so a sound caller-arena bound must scale with the Euclidean-chain length (not the per-op product envelope the other six peers use); shipping a gcd that could overflow a benign input would break standalone-complete honor, so Poly.gcd stays on the no-ceiling pure-bigint path (its inner long-divisions still route through srmech_poly_divmod when native). Verified: the bignum keystone — polys with 41-digit rational coeffs — match an independent fractions.Fraction oracle exactly for gcd/divmod/eval/shift; standalone test_srmech_poly.c smoke 7/7 pedantic -Werror (C-only host); WSL native Python==C parity 49/49 (the byte/value-identical keystone) + a 2000-case randomized stress (add/sub/mul/eval exact, divmod + shift invariants hold, zero overflow); the full ratchet/JPL/Rosetta/coverage gate green. Tests in test_poly_rc38.py (49). 5-SSOT 0.9.0rc37 → 0.9.0rc38.
[0.9.0rc37] - 2026-06-22¶
FEATURE (PR #687 §75 / F928) — coupling.resonant_spectrum: the "coupling the_one", the spectral row of the closure-dispatch, with a 1:1 C peer. Every Class-L spectral-kernel cascade srmech runs (language-usage kernel F920, cosmic-web eigen-environment F781, directional F926, gravity-coupling F927/F928) reduces to the SAME steps: build/take a coupling Laplacian L → eigensolve → read {the spectrum, the modes, the higher force-orders Lⁿ, the resonance ratios}. This crystallises that into ONE primitive — exactly as the_one crystallised the epicycle crank into S(σ,θ). srmech.amsc.coupling.resonant_spectrum(L, *, orders=2, max_den=64) returns a dict: tensions (Vec, eigenvalues ascending = the stored "dark" tension spectrum — present with no excitation, the MFO field reading F907); modes (Mat, eigenvectors = the excitation modes); force_orders ([L, L², …, Lᵒ], each Lᵏ = V·diag(Λᵏ)·Vᵀ reconstructed from the one eigensolve — L² = forces-of-forces = biharmonic = tidal); resonances (the integer/prime ratios of the tensions via best_rational + prime-coordinate factor, reading small-prime/2-adic = locked vs large-prime = libration off-lock). Composes SHIPPED ops only — laplacian.symmetric_eigendecompose (L), mat_matmul (L), rational.best_rational (N), primes.factor (J); numpy-free, math-free, no abs(). A companion coupling.from_bodies(masses, positions) → (n, edges, weights) builds the mᵢ·mⱼ/r² gravity Laplacian (a non_compute edge-builder, peer to the existing public cooccurrence_edges). 1:1 C peer (the everything-mirrors discipline — a C-only host calls it too): srmech_resonant_spectrum + srmech_resonant_spectrum_arena_bytes (c/src/srmech_coupling.c), a composite over the existing srmech_hermitian_eigendecompose_ws + srmech_best_rational + srmech_factor (no new eigensolver/matmul/factoriser), caller-arena (no malloc), JPL-clean (≤34-line funcs, ≥2 asserts). coupling.resonant_spectrum routes through it when HAS_NATIVE; pure-Python is the complete alternative. Verified: force_orders Λ²==L·L (1.9e-12); the F928 anchor — for the Jupiter+Galilean gravity Laplacian, L² concentrates on the Jupiter↔Io pair (~26× vs the outer Callisto coupling); Python==C value-parity on native (tensions 8.9e-16, modes 1.1e-15, force_orders 5.1e-13, all resonance pairs/ratios/lock-libration verdicts exact); standalone test_srmech_coupling.c smoke 11/11 pedantic -Werror (C-only operation); the full ratchet/JPL/Rosetta/coverage/MCP gate green. Two new ToolEntries (resonant_spectrum C-dispatched + from_bodies non_compute) → describe()["tools"]["total"] 320 → 322; ABI stays 3 (additive C symbols). Tests in test_resonant_spectrum_rc37.py. 5-SSOT 0.9.0rc36 → 0.9.0rc37.
[0.9.0rc36] - 2026-06-22¶
BUGFIX (C shared infra) — srmech_bigint_pow_u32 no longer spuriously overflows for a small base with a large exponent. The caller-arena big-integer power op sized its internal square-and-multiply scratch (the running square b2 + the raw mul-temp t) as a fixed base->n*32 + 4 limbs — an implicit assumption that the exponent is ≤ ~32. Once base^exp exceeded 32 limbs (e.g. 7^600 = 53 limbs, 452^128), the running square overflowed its capacity and the op returned SRMECH_ERR_OVERFLOW even when the caller supplied a large-enough out — a magnitude ceiling that Python's int(base)**exp does not have (the rc35 srmech_bigexp transcendental work deliberately routed around pow_u32 because of this). The scratch is now sized from the op's OWN advertised bound: b2 gets pow_bound(base->n, exp) limbs (the running square is <= base^exp) and the mul-temp t gets mul_bound(pow_bound, pow_bound) (the raw product space), with an explicit pow_bound-overflow guard. A new companion srmech_bigint_pow_ws_bound(base_n, exp) returns the exact workspace bytes a caller must provide, so the contract is no longer implicit (the standalone-honor discipline — a C-only host sizes its arena correctly). Keystone: srmech_bigint_pow_u32(7, 600) now returns the full 508-digit 7**600 byte-identical to Python (53 limbs, past the old 36-limb internal cap), verified in test_srmech_bigint.c (now 47/47, pedantic -Werror) plus the tight-pow_ws_bound path. pow_u32 had no existing caller (it was a dead exported building block — the bug was latent), so nothing regressed: the rc35 test_srmech_bigexp smoke stays 20/20 and the full transcendental parity is unchanged. The new pow_ws_bound symbol is additive → SRMECH_ABI_VERSION stays 3; no Python source touched → describe()["tools"]["total"] stays 320. JPL audit 6/6 (both functions ≤60-line, ≥2 asserts, no malloc/goto). C-only change; pure-Python path unaffected. 5-SSOT 0.9.0rc35 → 0.9.0rc36.
[0.9.0rc35] - 2026-06-22¶
BUGFIX — C-bignum transcendental series: a C-only host now has Python's unbounded exact reach, closing the int64/Q61 1:1-mirror-parity violation. srmech's exact-rational transcendental series are exact-rational-in → exact-rational-out. On the Python side (srmech.amsc.rational.{exp,sin,cos,log1p,atan}_series_truncate + rational_pow_uint) the output is an arbitrary-precision bignum (num, den) — no ceiling. But the existing C peers were int64/Q61-bounded (srmech_exp_series_truncate emits SRMECH_ERR_OVERFLOW past int64; the *_q61 peers cap |x| < 2^55), so a C-only host (microcontroller / no Python) hit a magnitude ceiling Python does not — a 1:1-parity violation ([[feedback_c_must_be_standalone_complete_no_python_fallback]]). This rc adds bignum-exact C variants computed over the existing caller-arena srmech_bigint: srmech_exp_series_truncate_big, srmech_sin_series_truncate_big, srmech_cos_series_truncate_big, srmech_log1p_series_truncate_big, srmech_atan_series_truncate_big, srmech_rational_pow_uint_big, plus the srmech_bigexp_ws_bound caller-arena byte-sizer (all in c/src/srmech_bigexp.c). Operands/results pass as srmech_bigint_t pairs; every working carrier + divmod/gcd scratch is carved from a caller arena (no malloc, no goto, ≤60-line functions, ≥2 asserts each — JPL-clean); output is gcd-reduced to lowest terms with positive denominator, matching Python's (num, den) convention byte-for-byte. sqrt needed no new symbol — the Python exact path is srmech_bigint_isqrt-based, already present. Keystone (proven exp-first): exp(7/3, N=30) → exact (234562913…469, 22746027…000), a ~148-bit numerator over a ~145-bit denominator (both > 2^64), equal to rational.exp_series_truncate(7,3,30) exactly; a 260-case randomised sweep across all six ops (each with a >2^64 case) passed with zero mismatches. The new symbols are additive → SRMECH_ABI_VERSION stays 3 (bound in _native.py via hasattr); describe()["tools"]["total"] stays 320 (C-surface additions are not ToolEntries). Verified: WSL gcc -Wall -Wextra -Werror -pedantic clean + a standalone test_srmech_bigexp.c smoke (20/20, C-only with no Python); the new test_c_bignum_transcendentals_rc35.py (38 parity cases incl. per-op >int64 keystones) skips cleanly when native is absent and passes 38/38 when the native lib is loaded (CI builds native → CI exercises the parity); test_jpl_audit.py 6/6. Note (slated for a follow-up rc): the shared srmech_bigint_pow_u32 sizes its internal square-and-multiply scratch as base->n*32+4 limbs, which overflows once base^exp exceeds 32 limbs (e.g. 452^128) — a latent defect in shared bigint infrastructure (its own advertised bound is base_n*exp+1); this rc routes around it with a local square-and-multiply over the large context carriers rather than touching shared infra, and flags the fix for its own focused ship. 5-SSOT 0.9.0rc34 → 0.9.0rc35.
[0.9.0rc34] - 2026-06-22¶
FEATURE — QMat, the exact-rational matrix carrier: closes the exact-array gap so the bigint exact-carrier suite spans scalars AND matrices. The exact SCALAR carriers (Q exact-rational, Qi Gaussian-rational, Qalg ℚ(α) number field, Qprime prime-coordinate) are all Python-int-backed = arbitrary precision (bigint), no magnitude ceiling; but exact LINEAR ALGEBRA (eigvec_exact, the rc31-33 exact RREF / so(8) companion solve) rode ad-hoc nested Fraction lists with no carrier. srmech.amsc.QMat is the missing piece — the exact-rational dense matrix, a 2-D grid of Q, the bigint exact peer of the float64 Mat. Surface: from_rows / from_float_rows / from_mat / identity / zeros; shape / __getitem__ / to_lists / __eq__; exact + - neg, scalar *, @ (matmul), transpose / .T; and the exact-ℚ linear algebra — rref, rank, det, inverse, solve, nullspace (all on one shared exact Gauss-Jordan kernel, pivots by Q != 0, sign as an integer ±1 Class-K pin-slot, never abs()/float()). The one boundary collapse is to_mat() → float64 Mat (the explicit ALU→FPU rotation). The keystone: no magnitude ceiling — a QMat with entries far beyond int64 (e.g. Q(10**40+1, 3**30)) keeps matmul/det/inverse EXACT (det carries an 81-digit numerator over a 3**60 > 2**64 denominator, matched against a fractions.Fraction oracle), where the native int64 Q61 path could not. QMat is a carrier class (peer to Qi/Qalg/Qprime), so — like them — it is NOT a ToolEntry and is invisible to the inspect.isfunction coverage walk: describe()["tools"]["total"] stays 320, ABI 3, Python-only (exact linear algebra, like the exact eigensolver, is Python by scope — a native exact-matrix mirror is a future arc). Re-exported as srmech.amsc.QMat; tests in test_qmat_carrier_rc34.py (32 cases, numpy-free + math-free, including the bigint keystone). 5-SSOT 0.9.0rc33 → 0.9.0rc34.
[0.9.0rc33] - 2026-06-22¶
BUGFIX — the so(8)/triality companion solve is now EXACT over ℚ, closing the native-vs-pure rank divergence. srmech.qm.triality._solve_companions solved the rank-deficient triality companion normal equations G·x = c with the float mat_solve. The native path tolerated the singular Gram, but the pure-Python (pure-wheel / WASM / numpy-absent) path applied a Tikhonov ridge λI (λ ≈ 1e-12·max diag) — biasing the solution by ~6e-11. That drift is harmless to the float geometry tests (their tolerance is 1e-9), but the so(8) basis-rank machinery reduces dimensions through the EXACT so8._rank_exact (rational RREF, no tolerance), which read the ~6e-11-drifted near-±1/2 entries of tau/S_B as distinct rationals — over-counting rank, so Fix(tau) came out 0 instead of 14 and Fix(S_B) 0 instead of 21 whenever native was absent (test_so8_triality.py::test_killer_fix_tau_is_g2_dim14 + test_fix_z2_swap_is_so7_dim21 passed in CI but failed pure). The octonion structure constants are {-1, 0, +1} integers, so G and c are an INTEGER system — it is now solved EXACTLY by a new _exact_solve_normal_equations (fractions.Fraction Gauss-Jordan; the consistent system's free/gauge columns are pinned to 0, giving a residual-0 particular solution). The companion maps come out exact dyadic rationals (denominators in {1, 2}), so S_B / S_C / tau are bit-identical on every platform — Fix(tau) = 14 / Fix(S_B) = 21 regardless of native. The exact solution reproduces the SAME gauge as the float construction (tau matches the prior float tau to ~2e-12), so all six acceptance tests (order-3, killer Fix = g2, Fix(Z2) = so(7), Cartan residual, rep-cycle, reproducibility) are unchanged — only now exact and native-independent. No float mat_solve and no ridge remain in the triality construction; mat_solve dropped from the module import. Carrier/internal change only — tools.total stays 320, ABI 3, Python-only. Tests: the full test_so8_triality.py cluster now passes in a numpy-absent / native-absent venv. 5-SSOT 0.9.0rc32 → 0.9.0rc33.
[0.9.0rc32] - 2026-06-22¶
FEATURE — the §74 / F923 CAPSTONE: the Qprime prime-coordinate exact carrier closes the LAST harmonic-ladder rung (Class J). A new exact carrier srmech.amsc.qprime.Qprime (peer to Qi / Qalg) carries a positive integer in its prime-coordinate representation — the exponent vector {prime: exponent} of the fundamental theorem of arithmetic n = ∏ pᵉ. The lens: multiplication is addition in prime-coordinates. Qprime(n) factors via the Class-J primes.factor; to_int() reconstructs ∏ pᵉ; coords is the read-only exponent dict. The exact operations compose primes.factor + cyclic.gcd/lcm + primes.cyclic_period (no new C — those already have native C): * [Class J] adds exponents elementwise (== factor(a·b)); gcd is elementwise MIN over the shared support (== cyclic.gcd); lcm is elementwise MAX over the union (== cyclic.lcm); similarity(other) is the EXACT shared-factor overlap — cosine² of the exponent vectors as an exact Q (Fraction(dot², ‖a‖²·‖b‖²)), 0 for a coprime pair (Qprime(12).similarity(Qprime(18)) == Q(16, 25) EXACT); period(modulus) [Class J period structure] is the multiplicative order ordₘ(n) via primes.cyclic_period (the 1/m repeating-decimal-period lens, Qprime(10).period(7) == 6), gcd-guarded (raises ValueError when gcd(n, modulus) != 1). Qprime(1) is the empty vector / identity; Qprime(0) and negatives raise ValueError. Verified over a 200-pair exact battery (round-trip + multiply=add-exp + gcd=min + lcm=max). Closes the Class J harmonic-ladder rung — HARMONIC_LADDER_OPEN_RUNGS is now {2: (), 3: ()}, EMPTY: no encode blind spots remain (C/K closed rc31 via Qi, J closed here via Qprime). A new derived predicate harmonics.harmonic_ladder_fully_closed() now returns True (the capstone). Qprime is a carrier (Python, like Qalg/Qi), so it is NOT a ToolEntry — tools.total stays 320, ABI 3, Python-only (no C touched but the srmech.h version macro). Also: a §74 doc fix — the FACTOR_MAX_DISTINCT_PRIMES comment in primes.py had 6.14e16 for the product of the first 15 primes; the value is 6.14e17 (614889782588491410), corrected (doc only, no behavior change). Tests: test_qprime_carrier_rc32.py. 5-SSOT 0.9.0rc31 → 0.9.0rc32.
[0.9.0rc31] - 2026-06-22¶
FEATURE — the Qarg graduation: exact POLAR accessors on the Qi exact-complex carrier (§74 / F924). Qi gains five methods — modulus() [Class K magnitude], arg() [Class C orientation], as_polar, from_polar, from_complex — composing the exact-Q sqrt/atan2/cos/sin already shipped in srmech.amsc.rational (re-exported by srmech.asymptotic_calculus). No new transcendental code and no new C — those four ops already accept and return exact Q. modulus() is the √ of the carrier's own exact norm_sq() (so Qi(3,4).modulus() == Q(5) EXACT), arg() is atan2(im, re) with quadrant logic as Class-C direction over Class-K signs (never an ALU abs()), from_polar(r, θ) reconstructs via r·(cos θ + i·sin θ) over Q arithmetic, and from_complex(z) lifts a builtin complex into the exact carrier (the bridge that lets Mat/Vec complex entries — e.g. a magnetic_laplacian off-diagonal — be polar-read exactly). Closes the harmonic-ladder Class C + Class K open rungs (HARMONIC_LADDER_OPEN_RUNGS[2] now (); J remains for the rc32 Qprime): on a directed 3-cycle's magnetic Laplacian, reversing every edge gives arg_fwd + arg_rev == Q(0) EXACT (the chirality flip — Class C recovers which-way) while modulus_fwd == modulus_rev (direction-blind — Class K recovers how-much). Round-trip residuals 0–1e-15. Qi is a carrier (Python, like Qalg), so these are NOT ToolEntries — tools.total stays 320, ABI 3, Python-only.
[0.9.0rc30] - 2026-06-22¶
CLEANUP + BUGFIX (no-stubs-ever pass; no behavior bugs except the §A hot-path fix). Three labeled tracks:
(A) BUGFIX — sim_k4_batch float hot-path (srmech/rbs_lm/substrate.py). The docstring promised "one float per candidate" but the body returned [hdc.klein4_similarity(query, c) for c in candidates], and since the rc7 stay-rational arc klein4_similarity returns an exact Q rational — so the inference vocab-ranking hot-path (rbs_lm/inference.py:189, the only caller) allocated a Q per vocab candidate. The float-batch hot-path was never landed. Fixed to D = len(query); return [hdc.klein4_match_count(query, c) / D for c in candidates] — the integer match-count (klein4_match_count, the raw count klein4_similarity = count / D exactly) divided once → one Python int/int float, skipping the per-candidate Q. The ranking argmax/sort order is unchanged. The existing test_sim_k4_batch_self_is_one uses pytest.approx(1.0) + inequalities (no Q-exact assertion), so it stays valid for floats.
(B + C) CLEANUP — deleted 15 dead Python symbols + 2 Phase-8 profiling stubs (re-grep-confirmed zero callers each):
- srmech/amsc/hdc.py: _as_klein4 (dead thin alias of the live _as_klein4_buf), _klein4_chirality_duals, _klein4_similarity_native (the public klein4_similarity routes via _klein4_match_count_core → _klein4_match_count_native, NOT this wrapper; the native srmech_klein4_similarity C symbol stays bound in _native.py + gated by has_native_klein4_bind, so deleting this Python wrapper orphans no native code).
- srmech/amsc/mat.py: Mat._flat_index (dead method).
- srmech/qm/so8.py: _mat_rows (dead m.tolist() wrapper).
- srmech/amsc/rational.py: _principal_angle_anchor (dead range-reduction helper; its sole-purpose default-arg constant _TRIG_FLOAT_ANCHOR_DEN removed with it — sibling _TRIG_FLOAT_TERMS/_ATAN_FLOAT_TERMS are live and kept), _q_scale2 + _q_pow2 (the pair — _q_pow2's only caller was _q_scale2), _rational_sqrt_midpoint (dead; its "Used by pi_cascade_digits" docstring was stale — pi_cascade_digits uses _integer_sqrt/_scaled_integer_sqrt).
- srmech/amsc/laplacian.py: _vec_to_interleaved_cbuf + _vec_from_interleaved_cbuf (unused Vec native-marshallers; the live Mat twins _mat_to_interleaved_cbuf/_mat_from_interleaved_cbuf are untouched).
- srmech/signal_processing/profiling.py: _time_one_call (dead Phase-1 unit-test utility); and the 2 Phase-8 STUBS profile_op + update_dispatch_table that unconditionally raise ProfilingNotImplementedError (the "Phase 8 (v0.4.2rc8)" runner never landed) — removed per the no-stubs rule, along with the ProfilingNotImplementedError exception (no other user), their __all__ entries in profiling.py + signal_processing/__init__.py, the 2 Rosetta non_compute ledger rows, the pinning tests test_profile_op_raises_in_phase_1/test_update_dispatch_table_raises_in_phase_1, and the dead-API mentions in cascade_dispatcher.py/_paths.py/the scaffolding-test docstring. (The profiling stubs had NO ToolEntry registration → describe()['tools']['total'] is unaffected.) Orphaned imports scrubbed (time, Callable, asdict).
(D) deleted 3 dead C constructs (ABI stays 3 — no bound symbol removed, no wire-format change): srmech_toml_canonical_hash orphaned (planned) prototype in c/include/srmech.h (no definition in any c/src/*.c); the dead SRMECH_LAPLACIAN_MAX_NODES #define in c/include/srmech.h (0 code expansions after the rc156-161 arena-carve sweep — the degree[256]/d_inv_sqrt[256] stack arrays are gone) + its 2 stale comments (srmech.h, srmech_laplacian.c:470); the unused SRMECH_BUS_MAX_FRAME_BYTES #define in c/src/srmech_bus.c.
No signature change → tools.total stays 320, ABI 3. numpy-free AND math-free, MIT. 5-SSOT 0.9.0rc29 → 0.9.0rc30. Tests: test_cleanup_rc30.py.
[0.9.0rc29] - 2026-06-22¶
POLISH (no behavior bugs — quality/cleanup).
-
mat_eigvalsParlett–Reinsch RADIX-2 balancing pre-step (srmech/amsc/laplacian.py). The float shifted-QR eigensolver (post-rc26 with EISPACK exceptional shifts) is correct but was UNBALANCED — a badly-scaled matrix loses accuracy. A new_balance_radix2(H)pre-conditionsHwith an EXACT diagonal similarityD⁻¹·H·Dthat equalises each index's row-norm against its column-norm. The scale factors are POWERS OF TWO only, so every multiply/divide is an exact binary mantissa-shift (no floating rounding) and the eigenvalue multiset is INVARIANT — unchanged for well-scaled input, MORE ACCURATE for badly-scaled input. The standard Parlett–Reinsch test iterates per index (while c < r/β: c·=β²; f·=β+ the symmetric branch, β=2), accepting a step only when it reducesr + c; sweeps repeat until no index changes. Norms use the Class-K_modulus_cmagnitude (no bareabs()). The exceptional-shift QR is unchanged (balancing is purely a pre-step onH). Refs: Parlett & Reinsch, "Balancing a matrix for calculation of eigenvalues and eigenvectors", Numer. Math. 13 (1969) 293–304; Golub & Van Loan §7.5.1. Validated: the rc26 battery (companions of x³−1/x⁴−1/x⁴+1/x⁵−1, rotation→{±i}, symmetric, defective[[2,1],[0,2]], nilpotent) multiset still== eigvals_exact(A, include_complex=True)to ~1e-9 (NO REGRESSION); a deliberately diagonal-similarity-scrambled known spectrum is closer to the exact eigenvalues WITH balancing than without (IMPROVED).mat_eigvalsstaysPRIMITIVE_NAin the rotation-last ledger (note updated: now balanced; the radix-2 similarity is exact so it adds NO rotation — avoidable-violation count stays 0). -
Deleted the dead
_certify_complex_root(srmech/amsc/cascade/matrix_cascades.py). rc28 routed the whole complex-root branch through the always-terminating per-square-free-factor_isolate_complex_roots_upper, dropping the only caller of the float-QR candidate accelerator_certify_complex_root. The function was orphaned (no caller insrmech/ortests/). It is removed, and its now-dangling references (the section comment, theeigvals_exactdocstring prose, the_isolate_complex_roots_upperdocstring, and the per-factor branch comment) are reworded to state that the complex path now ALWAYS uses pure exact argument-principle subdivision per square-free factor, with NO float-QR candidate seeding. The shared helpers (_refine_box,_modulus,_modulus_c,_root_free_split,_count_roots_in_box) are untouched. The historical-bug docstring intest_eigvals_repeated_complex_rc28.pyis left as-is (it correctly describes the OLD bug).
No signature change → tools.total stays 320, ABI 3, Python-only (only the c/include/srmech.h version macro is touched in C). numpy-free AND math-free, MIT. 5-SSOT 0.9.0rc28 → 0.9.0rc29. Tests: test_mat_eigvals_balancing_rc29.py.
[0.9.0rc28] - 2026-06-22¶
BUGFIX — eigvals_exact(include_complex=True) HUNG on a REPEATED complex eigenvalue (the companion of (x²+1)² → ±i each multiplicity 2; likewise (x²+1)³, (x−1)(x²+1)²). The REAL eigenvalue path was already correct — it iterates _square_free_factors(p) and isolates each square-free factor's (simple) real roots, appending each with multiplicity. The COMPLEX path did NOT: it isolated roots on the FULL characteristic polynomial p and expected to find want_upper = n_complex//2 SIMPLE roots. But box-subdivision CANNOT separate COINCIDENT roots — for a repeated complex root _count_roots_in_box returns the multiplicity (≥ 2) in EVERY enclosing box, never 1 → _certify_complex_root shrank its box forever (4000-try ceiling → fell back to subdivision) and _isolate_complex_roots_upper subdivided the same region forever with exploding Fraction denominators (the 200000-guard practical hang).
Fix: the complex branch now mirrors the real path — it iterates _square_free_factors(p) and, for each (factor, mult), isolates factor's upper-half (im > 0) complex roots. A square-free factor has ALL-SIMPLE roots, so box-subdivision ALWAYS terminates; each isolated upper root is emitted mult times (and its conjugate mult times), exactly as the real path appends each simple real root mult times. The per-factor upper-half count is (deg(factor) − #real_roots(factor)) // 2 (reusing _isolate_real_roots). The reconciliation assert (summed per-factor multiplicities == n_complex // 2) is kept as a post-check.
The float-QR candidate accelerator (_certify_complex_root) is DROPPED on the complex path (correctness + termination over speed): it must NEVER run on a non-square-free polynomial (it would loop on a coincident pair), and re-mapping float candidates to square-free factors is fiddly, so the whole branch routes through the always-terminating per-factor _isolate_complex_roots_upper. Defense-in-depth: _isolate_complex_roots_upper's subdivision guard is lowered 200000 → 20000 so a genuinely-pathological (mis-passed non-square-free) input raises in seconds rather than exploding Fraction denominators — the legit simple-root cases all pass well under the new bound. (_certify_complex_root keeps its 4000-try ValueError ceiling.)
Verified: companion of (x²+1)² → {i,i,−i,−i}, (x²+1)³ → ±i mult 3, (x−1)(x²+1)² → {1,i,i,−i,−i} — all now sub-second; the simple-complex regressions ((x²+1)(x²+4), rotation [[0,−1],[1,0]], x³−1, x⁴+1) unchanged; every case cross-checked against eig_exact (the independent per-irreducible-factor oracle). The rc27 eigvals_exact cross-check that had been EXCLUDED for the repeated-complex (x²+1)² case in test_jordan_exact_rc27.py is RE-ENABLED. No signature change → tools.total stays 320, ABI 3, Python-only (only the c/include/srmech.h version macro is touched in C). numpy-free AND math-free, MIT. 5-SSOT 0.9.0rc27 → 0.9.0rc28. eigvals_exact stays CANONICAL in the rotation-last ledger (note updated: complex path now square-free-factored; avoidable-violation count stays 0). Tests: test_eigvals_repeated_complex_rc28.py.
[0.9.0rc27] - 2026-06-22¶
The rotation-last roadmap's rc-G (the LAST gap): exact generalized eigenvectors / Jordan canonical form for DEFECTIVE (non-diagonalizable) matrices — the exact eigensolver CLOSES. Before rc27, eig_exact returned only the GEOMETRIC eigenvectors for a defective eigenvalue (fewer than n vectors) + a defective=True flag; Jordan generalized eigenvectors were out of scope. This rc computes them, so the result is a COMPLETE basis even when the matrix is not diagonalizable. Three new package-level ops in srmech/amsc/cascade/matrix_cascades.py (peers of eigvec_exact/eig_exact):
jordan_chains_exact(a, lam)— the exact Jordan structure for an eigenvalue λ (aQalgover its irreducible min-poly, alg-mult μ). WithN = A − λI,Nis nilpotent on the generalized eigenspacenull(Nᵘ)(dim μ); the block sizes are read off the EXACTQalg-Gaussian-elimination ranksr_k = rank(Nᵏ)(# blocks of size exactly k =r_{k-1} − 2·r_k + r_{k+1}), and the chains are built top-down (v, N·v, …, N^{p-1}·v, bottom = a geometric eigenvector). Returns(chains, block_sizes)— each chain alist[list[Qalg]]bottom→top withN·chain[i] == chain[i-1], VERIFIED exactly overQalgbefore returning. Reuses the rc23 exact-QalgGaussian-elimination null-space/rank machinery. Refs: Horn & Johnson, Matrix Analysis 2nd ed. §3.1–3.2; Golub & Van Loan §7.6.5.eig_exact(a, *, bits=64, project=True)— each eigenvalue dict now also carries"jordan_blocks": list[int](block sizes for that λ; all 1s ⇒ diagonalizable-at-this-λ) and"generalized_vectors"— the FULL μ-many generalized eigenvectors organized by chain (float/complex whenproject=True,Qalgwhenproject=False). For a non-defective λ the chains are all length 1, sogeneralized_vectors== the geometric eigenvectors and existing behaviour is preserved; for a defective λ this is the new complete basis. The rc25 dict keys (value/vector/algebraic_multiplicity/geometric_multiplicity/defective/min_poly) are unchanged (additive only). Self-validation now also asserts the FULL generalized basis has exactlynvectors (the complete-basis guarantee) ANDA·P ≈ P·Jto ~1e-9 whereJis the Jordan form.jordan_form_exact(a, *, bits=64, project=True)— the capstone: returns{"blocks": list[(eigenvalue, size)], "P": the n×n generalized-eigenvector matrix (columns), "J": the Jordan matrix}(float/complex whenproject=True;Qalg/exact whenproject=False). Self-validatesA·P == P·JEXACTLY overQalg(no float in the exact check) AND to ~1e-9 in float. Every square integer/rational matrix → exact Jordan canonical form, diagonalizable or not, real or complex (including complex-defective: the companion of(x²+1)²→±ieach a size-2 defective block).- Verified: defective
[[2,1],[0,2]]→ one size-2 block, chain{[1,0],[0,1]}with(A−2I)·top == bottom,J=[[2,1],[0,2]],A·P==P·Jexact;[[2,1,0],[0,2,0],[0,0,2]]→ blocks{2,1}(3 generalized vectors);[[5,1,0],[0,5,1],[0,0,5]]→ block[3](chain length 3, two super-diagonal 1s); diagonalizable[[2,0],[0,3]]/2·I→ all blocks[1],defective=False, rc25 output unchanged; complex-defective companion of(x²+1)²→±ieach size-2 defective. Cross-checked againsteigvals_exact(include_complex=True).
Closes the exact-substrate eigensolver: every square integer/rational matrix → exact Jordan form, diagonalizable or not. Package-level: all three return Qalg/complex (not MCP-serializable, like eigvec_exact/eig_exact), so they are NOT registered as ToolEntries → tools.total stays 320; all three added to the tool-schema-coverage exempt list + the Rosetta bignum_reference ledger + the rotation-last ledger (CANONICAL; avoidable-violation count unchanged at 0). ABI stays 3. numpy-free AND math-free, MIT. Python-only rc (only the c/include/srmech.h version macro is touched in C). 5-SSOT 0.9.0rc26 → 0.9.0rc27. Tests: test_jordan_exact_rc27.py.
[0.9.0rc26] - 2026-06-22¶
BUGFIX — mat_eigvals shifted-QR stalled → returned all-zeros on cyclic-permutation / equal-modulus spectra. The float shifted-QR mat_eigvals (srmech/amsc/laplacian.py) silently returned an all-zero spectrum for companion / cyclic-permutation matrices — e.g. the companion of xⁿ − 1 (the roots of unity: x³−1 → should be {1, ω, ω²}, also x⁴+1, x⁵−1, x⁶−1). Root cause: the single-shift Wilkinson loop computed the shift μ from the trailing 2×2, which for a companion block is [[0,0],[1,0]] → both eigenvalues 0 → μ = 0 → the step is effectively UNSHIFTED → an equal-modulus spectrum (all |λ| equal) never deflates → the loop ran out to the sweeps > max_sweeps·n backstop, which appended the diagonal H[i][i] — and a companion matrix's diagonal is all zeros → all-zero eigenvalues.
- The fix (classic EISPACK
hqr/ LAPACK exceptional-shift recipe; Golub & Van Loan, Matrix Computations 4th ed. §7.5): (1) a per-deflation-target STALL counterit, reset to 0 on every deflation (when the active block sizemdecreases); (2) an EXCEPTIONAL shift injected atit == 10andit == 20(the EISPACK cadence) that replaces the Wilkinsonμwith an ad-hoc shiftμ = |H[m-1][m-2]| + |H[m-2][m-3]|(just|H[m-1][m-2]|whenm-3 < 0) built from the local sub-diagonal magnitudes — enough to perturb the spectrum estimate and dislodge the equal-modulus lock (the cascade-honest Class-K_modulus_c, never a bareabs()); (3) a NON-SILENT failure mode — genuine non-convergence now raises a clearRuntimeError(pointing at the exact integer oracleeigvals_exact) instead of returning the garbage diagonal. The fast closed forms (n=1 scalar, trailing-2×2_eig2x2deflation) are unchanged; the signature staysmat_eigvals(a, *, max_sweeps=…)— internal fix only, no API change. - Validated HARD against the rc25 exact integer oracle
cascade.matrix_cascades.eigvals_exact(A, include_complex=True)(exact char-poly + argument-principle-certified roots — it never stalls): the previously-failing cyclic / cyclotomic cases (companions of x³−1, x⁴−1, x⁴+1, x⁵−1, x⁶−1) now match the oracle multiset to ~1e-9; rotation[[0,-1],[1,0]]→{±i}; hand-picked deterministic integer 3×¾×⅘×5 matrices with mixed real+complex spectra match; a symmetric integer matrix cross-checks vseigvals_exactANDjacobi_eigvals; a defective[[2,1],[0,2]]→{2,2}(multiset with the repeat). An explicit regression test asserts a cyclic-permutation companion is NOT all-zeros.
Package-level: internal fix, no new op → tools.total stays 320; ABI stays 3. mat_eigvals stays PRIMITIVE_NA in the rotation-last ledger (still a float FPU last-mile — just convergent now; verdict unchanged, avoidable-violation count unchanged at 0; ledger note updated). numpy-free AND math-free, MIT. Python-only rc (only the c/include/srmech.h version macro is touched in C). 5-SSOT 0.9.0rc25 → 0.9.0rc26. Tests: test_mat_eigvals_qr_shift_rc26.py.
[0.9.0rc25] - 2026-06-22¶
The rotation-last roadmap's rc-F: irreducible polynomial factorization over ℚ + the turnkey one-call eig_exact(a) — the exact eigensolver CLOSES into a single API, and exact COMPLEX eigenVECTORS are now reachable. Two new package-level ops in srmech/amsc/cascade/matrix_cascades.py (peers of eigvals_exact/eigvec_exact):
factor_integer_poly(coeffs)— factor an integer polynomial (coeffs low→high) into its IRREDUCIBLE factors over ℚ by Zassenhaus (Gauss's lemma: factoring over ℚ ≡ over ℤ). Returnslist[tuple[factor_coeffs, multiplicity]], each factor a primitive irreducible integer polynomial (content 1, positive lead);Π factor**multreconstructs the input up to sign/content (asserted by an internal self-check). Pipeline: content/primitive split → Yun square-free decomposition (reuse_square_free_factors) → factorf mod pin 𝔽_p[x] (distinct-degree + Cantor–Zassenhaus equal-degree) for a good primep ∤ lead(f)withfsquare-free modp→ Hensel-lift to modp^k ≥ 2·B+1(Bthe Mignotte coefficient bound) → recombine over increasing subset sizes (multiply modp^k, symmetric integer reps, scale by the leading-coeff cofactor, trial-divide over ℤ), guarded by a subset-size cap so the worst case cannot hang. All exact integer/Fraction(gcd viasrmech.amsc.cyclic.gcd, primality viasrmech.amsc.primes.is_prime); no float, nomath. Refs: Knuth TAOCP Vol. 2 §4.6.2; von zur Gathen–Gerhard Modern Computer Algebra ch. 15–16.eig_exact(a, *, bits=64, project=True)— turnkey:char_poly(a)→factor_integer_poly(the irreducible min-polysm_iwith algebraic multiplicities) → isolate ALL of each factor's roots (real via the Sturm cascade, complex via the rc-E argument-principle box subdivision) → each root →Qalg.alpha(m_i, embedding=root)→eigvec_exact(a, λ)for the exact eigenvector(s) = null space ofA − λIover ℚ(λ). Returns one dict per DISTINCT eigenvalue:{"value": complex, "vector": list[complex], "algebraic_multiplicity": int, "geometric_multiplicity": int, "min_poly": tuple[int], "defective": bool}—value/vectorthe single terminal float/complex projections (rotation-last),min_polythe eigenvalue's exact irreducible substrate.project=Falseinstead hands backvalue_qalg+vectors_qalg(the exactQalgobjects). Defective matrices (geometric < algebraic) return only the geometric eigenvectors withdefective=True; Jordan generalized eigenvectors are out of scope (documented, not fabricated). This makes exact COMPLEX eigenVECTORS reachable (e.g.[[0,-1],[1,0]]→{i,−i}with complex eigenvectors).- Self-validation (asserted before returning — catches a factorisation bug): (a)
Σ algebraic_multiplicity == n; (b) the eigenvalue multiset reconstructs the monic char-poly (Π(x − value)≈ char-poly to ~1e-7, numeric); © every returned(value, vector)satisfiesA·vector ≈ value·vectorto ~1e-9. Any failure raises a clearValueError. - Verified:
factor_integer_polyon x⁴−1 → {(x−1),(x+1),(x²+1)}, x⁴+1 / x⁴−10x²+1 (√2+√3) / x³−2 irreducible, (x²+1)² → mult 2, x⁶−1 → 4 cyclotomic factors, (x−2)(x²+x+1) reducible.eig_exacton[[2,1],[1,2]]→ {1:[1,−1], 3:[1,1]},[[1,1],[1,2]]→ ℚ(√5) eigenvectors, rotation[[0,-1],[1,0]]→ COMPLEX {i,−i} with complex eigenvectors, companion x³−1 → {1,ω,ω²},2·I→ λ=2 (alg 2, geom 2, not defective), defective[[2,1],[0,2]]→ λ=2 (alg 2, geom 1, one eigenvector,defective=True). Cross-checked againsteigvals_exact(include_complex=True).
Closes the exact-substrate eigenproblem into a one-call API (alongside factor_integer_poly, which also retires the rc23 "reducible-m is the rc-E follow-up" gap — the char-poly is now factored INTO its irreducibles, so every eigvec_exact call inside eig_exact runs over a genuine field). Package-level: both ops return Qalg/complex (not MCP-serializable, like eigvec_exact), so they are NOT registered as ToolEntries → tools.total stays 320; both added to the tool-schema-coverage exempt list + the Rosetta bignum_reference ledger + the rotation-last ledger (CANONICAL; avoidable-violation count unchanged at 0). ABI stays 3. numpy-free AND math-free, MIT. Python-only rc. 5-SSOT 0.9.0rc24 → 0.9.0rc25. Tests: test_eig_exact_turnkey_rc25.py.
[0.9.0rc24] - 2026-06-22¶
The rotation-last roadmap's rc-E: exact COMPLEX eigenvalues — eigvals_exact(..., include_complex=True). srmech's eigvals_exact isolated only the REAL eigenvalues of an integer matrix (char-poly → Yun square-free → Sturm → Fraction bisection); this rc adds the COMPLEX ones, exactly isolated. The new kw-only include_complex (default False → current behaviour byte-for-byte unchanged) routes through the one hard exact primitive — _count_roots_in_box(p, x0, x1, y0, y1), the number of roots (with multiplicity) of an integer polynomial strictly inside an open rational box, by the argument principle (the winding number of p around the rectangular boundary) in EXACT integer/fractions.Fraction arithmetic, NO float in the count. Along each of the 4 edges p restricts to U(t)+iV(t) with U,V ∈ ℚ[t]; the boundary change-of-argument is the sum of the per-edge Cauchy indices of V/U (a Sturm-style generalised sign-variation count built from the same machinery as the real isolation), and −½·Σ_edges I is the exact non-negative integer root count.
_count_roots_in_box(p, x0, x1, y0, y1)— the load-bearing exact primitive (unit-tested HARD on its own):x²+1→ 1 around+i, 1 around−i, 0 around0, 2 over a big box;x²−2→ boxes that do/don't contain√2;x⁴+1→ 1 in each of the 4 quadrant boxes arounde^{iπ/4·{1,3,5,7}}. A half-integer winding (a root ON the boundary) raises so the caller nudges the rational corners.eigvals_exact(a, *, bits=64, return_intervals=False, include_complex=False)— withinclude_complex=True: real eigenvalues via the existing exact path; complex eigenvalues seeded from the float-QRmat_eigvalscandidates and CERTIFIED (each grown/shrunk to a rational box with_count_roots_in_box == 1, then refined tobits), with a pure argument-principle subdivision fallback (_isolate_complex_roots_upperfrom the Cauchy root bound) for when float-QR stalls (it does — e.g. on a cyclic permutation matrix, wheremat_eigvalsreturns all-zeros forx³−1/x⁴+1). The emittedcomplexis the single terminal projection of the certified box center — the exact-substrate object is the integer char-poly + the certified isolating box. Conjugate symmetry is exact by construction (a+bimirrored toa−bioff the same box center). Returns allneigenvalues with multiplicity: reals first (ascending,float), then complex (sorted by(re, im),complex). Distinguished honestly from the unconditioned float-QRmat_eigvalsit merely seeds.- Verified:
A=[[0,-1],[1,0]](x²+1) →{±i}withinclude_complex=Falsestill[]; companion of x³−1 →{1, ω, ω²}(ω=(−1+√3 i)/2);(x−1)(x²+1)→{1, ±i};x⁴+1companion → the 4 primitive 8th roots; symmetric[[2,1],[1,2]]→[1.0, 3.0]unchanged. All complex values match the true roots to 1e-9.
Completes the exact eigenVALUE side (real + complex) of the rotation-last audit's eigenproblem (alongside rc-D exact eigenVECTORS). eigvals_exact stays CANONICAL in the rotation-last ledger (exact char-poly + certified box + one projection; avoidable-violation count unchanged at 0). Package-level: a new kw-only include_complex PARAM (not a new op), so tools.total stays 320; bool already has an MCP coercer + sample. ABI stays 3. numpy-free AND math-free (the argument-principle count is exact Fraction; only the terminal box-center read-out is a float), MIT. Python-only rc. 5-SSOT 0.9.0rc23 → 0.9.0rc24. Tests: test_eigvals_exact_complex_rc24.py.
[0.9.0rc23] - 2026-06-21¶
The rotation-last roadmap's rc-D: exact EIGENVECTORS via the Qalg carrier — the null space of A−λI over ℚ(λ) by exact Gaussian elimination. srmech already had exact eigenVALUES (eigvals_exact — char-poly Faddeev–LeVerrier → Yun square-free → Sturm isolation → Fraction bisection); eigenVECTORS were the gap the audit unlocked. For an integer/rational matrix A and an eigenvalue λ that is a root of an IRREDUCIBLE integer polynomial m, ℚ(λ) = ℚ[x]/(m) is a FIELD, so the eigenvector lives in the null space of A − λI over ℚ(λ). The new ops (in srmech/amsc/cascade/matrix_cascades.py, peers of eigvals_exact) represent λ as a Qalg over m, build M = A − λI with Qalg entries, and run EXACT Gaussian elimination over the Qalg field — every step is exact Q (rotation-last: the body stays exact; the ONE terminal rotation is .to_complex()/.to_float() per component). The Tajima–Ohara–Terui (arXiv:1811.09149) exact-eigenvector regime done as a direct exact null-space.
eigvec_exact(a, lam)—aan integer/rational square matrix (list-of-lists /Mat),lamaQalg(the eigenvalue, carrying its irreduciblem+ embeddingroot). Builds the n×nQalgmatrixM = A − λI(diagonal subtracts λ), runs exact RREF over theQalgfield (pivot → normalize by the pivot'sinverse()→ eliminate the column → track pivot vs free columns), and reads the null space off the free columns (each free column → one basis vector, the free variable set to 1, pivot variables back-substituted). For a SIMPLE eigenvalue (rank n−1 → 1-D null space) returns the single null vector as alist[Qalg]; for a DEGENERATE/repeated eigenvalue (null-space dim > 1) returns ALL basis vectors as alist[list[Qalg]]. The eigen-relationA·v == λ·vis verified EXACTLY overQalg(componentwise, all-Qalg equality) before returning. A reduciblem(ℚ[x]/(m) is then NOT a field → a zero-divisor pivot,Qalg.inverse()raises) is re-raised as a clearValueError(polynomial factorization of the char-poly is the rc-E follow-up).eigvec_exact_float(a, lam)— callseigvec_exact, then terminal-projects each component viaQalg.to_complex()(orQalg.to_float()whenlam.rootis real) → alist[complex]/list[float](shape-faithful: nested for the degenerate case). The one float-eval-per-component read-out (rotation-last).- Verified:
A=[[1,1],[1,2]](ℚ(√5),m=(1,-3,1)) gives the exact eigenvector[α−2, 1](∝[1, λ−1]) for both roots λ=(3±√5)/2; a 3×3 integer matrix with an irreducible cubic (companion of x³−x−1) gives an exact eigenvector matching an independent numeric eigenvector up to scale; rational eigenvalues (m linear, ℚ(λ)=ℚ) give purely-rational eigenvectors (A=[[2,1],[1,2]]: λ=3→[1,1], λ=1→[1,−1]); the reducible-m guard fires; the multi-dim null-space case returnslist[list[Qalg]].
Completes the audit's exact-substrate eigenproblem (exact eigenvalues eigvals_exact + now exact eigenvectors). Package-level: like the Qalg carrier itself these ops return Qalg (not MCP-serializable), so they are NOT registered as ToolEntries → tools.total stays 320; ABI stays 3. numpy-free AND math-free (the exact-Q bignum body never touches a float; only the terminal projection does), MIT. Python-only rc. 5-SSOT 0.9.0rc22 → 0.9.0rc23. Tests: test_eigvec_exact_rc23.py.
[0.9.0rc22] - 2026-06-21¶
The rotation-last roadmap's rc-C: Qalg, the exact ℚ(α) number-field carrier — the generalisation of Qi. Where Qi carries an exact element of the ONE field ℚ[x]/(x²+1) (Gaussian rationals = exact ℂ over ℚ), the new Qalg (srmech/amsc/qalg.py) carries an exact element of ℚ[x]/(m(x)) for ANY monic irreducible m ∈ ℤ[x] — an exact element of the algebraic number field ℚ(α) where α is a root of m. Qi is literally Qalg specialised to m(x) = x²+1 (the headline equivalence test): the Gaussian product (a+bi)(c+di) = (ac−bd)+(ad+bc)i IS polynomial-multiply-then-reduce-mod-(x²+1), exactly what Qalg.__mul__ does for general m. The exact-substrate carrier for algebraic numbers; the foundation for exact eigenvectors (rc-D).
- State (immutable):
m= the minimal polynomial as a tuple ofintcoefficients low→high, MONIC ((m₀, …, m_{n−1}, 1), length n+1);coords= a tuple ofQof lengthn = deg(m)(the elementΣ coords[i]·αⁱ); optional embeddingroot(a Pythonfloat/complex) used ONLY by the terminal projection. ConstructorsQalg(m, coords, root=),Qalg.alpha(m, root=)(the generator α),Qalg.rational(q, m, root=)(a constant). A_same_fieldguard raisesValueErroron mismatchedm. - Exact field algebra (embedding-agnostic, pure
Q):+/−/−xcoordinatewise (Class-K sign — noabs());*convolves the coord tuples then reduces modmvia the monic relationαⁿ = −Σ m[i]·αⁱ;inverse()//run the extended Euclidean algorithm on the coordinate polynomial andm(x)in ℚ[x] (u·b + v·m = g, a nonzero constant sincemirreducible +b ≠ 0⇒ coprime);**is integer-exponent square-and-multiply (negative viainverse(),k==0→ the field one); scalar*byQ/int/Fraction;__eq__/__hash__on(m, coords). Inverting 0 raisesZeroDivisionError. - Terminal projection — the ONE rotation:
to_complex()Horner-evaluates the exact coord polynomial atself.root(→ builtincomplex);to_float()the same for a real root (→float, raises on a non-real root / nonzero imaginary value). The single FPU lift; the body stays exactQuntil here. Exact root isolation/refinement is deliberately rc-E — for rc-C the embedding root is caller-supplied. Qi≡Qalgoverx²+1verified:+/−/×/inverse///**coords matchQi's(re, im)exactly, andQalg(...).to_complex()withroot=1jequalscomplex(Qi(...)). The √2 (m = x²−2) and ∛2 (m = x³−2) embeddings evaluate correctly;α**3 == 2in ℚ(∛2); the generator satisfies its own minimal polynomial (Σ m[i]·αⁱ == 0).
Qalg is a CARRIER, not a ToolEntry (mirrors Qi, which is also not registered) → tools.total stays 320; ABI stays 3. NOT registered with numbers.Complex/numbers.Number (the rc10/rc11 Fraction-protocol trap — a standalone exact carrier). numpy-free AND math-free (no float in the algebra, only at the terminal projection), MIT. Python-only rc (the exact-Q bignum arithmetic IS the reference; a native peer is a possible follow-up). 5-SSOT 0.9.0rc21 → 0.9.0rc22. Tests: test_qalg_carrier_rc22.py.
[0.9.0rc21] - 2026-06-21¶
The rotation-last roadmap's doc-cleanup + rc-B: opt-in exact symmetric eigenvalues. Two small, additive changes — a research-prose docstring sweep on the two pi_cascade signal-processing delegators, and a keyword-only exact= route on jacobi_eigvals that keeps an integer/rational symmetric spectrum exact until the single terminal float lift. No new public op (just a new optional param); float-Jacobi stays the default speed path verbatim.
- Chiral-pair delegator docstring cleanup. The Path A (
signal_processing/closed_form_ops/pi_cascade.py) and Path B (signal_processing/path_b_ops/pi_cascade.py) delegators still described the OLD linear hexagon-doubling Archimedes. Their research-prose docstrings are updated to describe the two-sided Pfaff–Archimedes chiral pair (harmonic-mean ↓ circumscribed + geometric-mean ↑ inscribed, bracketing π at the midpoint) thatamsc/rational.py:pi_cascade_digitsnow implements (rc20), notingpi_chudnovsky_digitsis the canonical exact-substrate π digit SSoT. DOC-ONLY — the delegators are unchanged thin wrappers over_amsc_pi_cascade_digits; no behaviour/code change. - rc-B:
jacobi_eigvals(…, exact=True)— opt-in exact symmetric eigenvalues. A new keyword-onlyexact: bool = Falseparameter.exact=False(default) is the current float-Jacobi path verbatim (zero behaviour change).exact=Truevalidates the matrix is square, every entry EXACT (int/fractions.Fraction/ srmechQ, never afloat/complex), and SYMMETRIC (a[i][j] == a[j][i]) — else a clearValueError— then routes to the exact-substrate cascadeeigvals_exact(lazily imported to avoid any circular-import risk) and returns aVecof the ascending eigenvalues with multiplicity (matching the float-Jacobi return contract). The rotation-last rationale: for integer/rational symmetric input the spectrum stays exact (char-poly Faddeev–LeVerrier → Yun square-free → Sturm isolation → Fraction bisection) until the single terminal float lift — the audit's "exact-substrate-achievable" case. The rotation-last ledger note on the_jacobi_*entries records this new exact route while the default-path VERDICT stays PRIMITIVE_NA (AVOIDABLE_VIOLATION count unchanged at 0).
NO new public op → tools.total stays 320; ABI stays 3. numpy-free AND math-free, MIT. 5-SSOT 0.9.0rc20 → 0.9.0rc21. Tests: test_exact_eigvals_routing_rc21.py, test_rotation_last_ratchet.py.
[0.9.0rc20] - 2026-06-21¶
The rotation-last audit, rc-A: pi_cascade_digits is rewritten from the naive linear single-bound Archimedes to the two-sided Pfaff–Archimedes chiral pair, and a new down-only rotation-last ratchet pins avoidable violations at 0. The audit's refined criterion: a mid-body rotation (continuous / FPU last-mile projection) is a VIOLATION only when an exact-substrate rotation-last form is achievable; an intrinsic-float step (π transcendental, iterative float eigensolvers) is PRIMITIVE_NA, not a violation. The audit found exactly ONE avoidable violation — the linear single-bound hexagon-doubling pi_cascade_digits (one rational √ feeding the next radicand at every step) — and it is removed.
pi_cascade_digits→ the two-sided chiral pair. The body is now the harmonic-mean (circumscribed, ↓ to π) / geometric-mean (inscribed, ↑ to π) bracket:a₀ = √12·M,b₀ = 3·M, thenaₙ₊₁ = 2·aₙ·bₙ/(aₙ+bₙ)andbₙ₊₁ = √(aₙ₊₁·bₙ)(the ONE integer-isqrt per step), with π read off as the bracket midpoint(a+b)/2. The bracket invariantb/M < π < a/Mholds at every step (b ↑, a ↓). Kept as the studied "ordered cycle-of-cycles" pattern; classified PRIMITIVE_NA (intrinsic-float LIMIT, π transcendental — both Archimedes forms carry a per-step √), NOT a violation. Same public signature / caps / auto-params / determinism /"3."+digits return; numpy-free, nomath. The 2 signal-processing delegators auto-follow (they call_amsc_pi_cascade_digits).- Chudnovsky stays the canonical exact-substrate π.
pi_chudnovsky_digits(rc19) — bit-exact integer body, ONE terminal rotation — is the digit SSoT the chiral pair is cross-checked against (chiral-pair == Chudnovsky byte-for-byte at 15 / 100 / 500 / 1000 digits). - New down-only rotation-last ratchet (
tests/rotation_last_classification.ndjson+tests/test_rotation_last_ratchet.py, peer to the Rosetta ratchet): one record per iterative numeric cascade tagged CANONICAL / PRIMITIVE_NA / AVOIDABLE_VIOLATION; the AVOIDABLE_VIOLATION count is pinned at 0 (the audit's guard — no cascade may rotate mid-body when an exact-substrate rotation-last form is achievable). The ledger is kept honest (every listed file exists, every op token appears in its file).
NO new public op → tools.total stays 320; ABI stays 3. numpy-free AND math-free, MIT. 5-SSOT 0.9.0rc19 → 0.9.0rc20. Tests: test_pi_chiral_pair_rc20.py, test_rotation_last_ratchet.py.
[0.9.0rc19] - 2026-06-21¶
srmech_bigint.c — a caller-arena arbitrary-precision integer foundation lands in C, and the rotation-last Chudnovsky π operator (pi_chudnovsky_digits) earns its native dispatch on it. The C library gains an unbounded-integer kernel — base 2^32 limbs, all scratch CARVED FROM THE CALLER ARENA (no malloc, no external bignum lib) — the standalone-complete foundation a C-only / microcontroller host needs for exact integer math past int64. It is carrier-internal (NO Python surface, NO ctypes binding). Built on it, the srmech_pi_chudnovsky C symbol runs the rotation-last Chudnovsky series and the public pi_chudnovsky_digits now dispatches to it.
- New public op
srmech.amsc.rational.pi_chudnovsky_digits(Class N; +1 → tools.total 320). The canonical srmech cascade shape: the BODY stays bit-exact (exact integer add/sub/mul/floor-divmod accumulating the Chudnovsky 1988 linear series onsrmech_bigint), and the SINGLE continuous/frame projection — the "rotation" — happens ONCE, terminally (isqrt(10005·one²)→ one division → base-10 render). NO float, NOmath, NO per-term square root — the opposite of the Archimedespi_cascade_digits, which projects every step. ~14.18 digits land per term. SSoT: D. V. & G. V. Chudnovsky, "Approximations and complex multiplication according to Ramanujan" (1988). - Native dispatch.
pi_chudnovsky_digitscallssrmech_pi_chudnovskywhenHAS_NATIVE(sizing the caller arena viasrmech_pi_chudnovsky_ws_bound), and the pure-Python arbitrary-precisionintbody is BOTH the no-C / Pyodide fallback AND the parity oracle the C path is checked against._native.pybinds the twosrmech_pi_*symbols (hasattr-guarded) +pi_chudnovsky_c/has_native_pi_chudnovsky. Validated C == Python == Archimedes oracle at 1000 and 10000 digits (10k in ≈0.5s; rotation-last = exact bigint body + one terminal isqrt+division).c_dispatchedin the Rosetta ledger.
ABI stays 3 (additive symbols, hasattr-bound). numpy-free AND math-free, MIT. 5-SSOT 0.9.0rc18 → 0.9.0rc19; tools.total 319 → 320. Tests: test_pi_chudnovsky_rc19.py.
[0.9.0rc18] - 2026-06-21¶
F917 Part B: the L1/L2/L3 ladder is rebuilt on klein4_compose, and the compositor earns its native C peer (srmech_klein4_compose). rc17 graduated the byte/glyph substrate (ContextSubstrate.enc) and shipped klein4_compose as a pure composition; this rc makes the whole ladder one scale-invariant operator and gives the compositor a single-call native dispatch.
- The ladder is rebuilt on the role-filler compose.
encode_bigram_l1/encode_skeleton_l2/encode_sentence_l3replace their chainedklein4_bindfold (involutive → a one-word change collapsed it toward the ~0.25 chance level) withhdc.klein4_compose— the SAME compositor at every rung (byte→word→phrase→sentence; the L2 parts are the L1 composed vectors). A one-word change now degrades GRACEFULLY (measured: a sentence one-word edit retainssim≈0.78vs the chained fold's≈0.22 ≈ chanceover the same word-hash atoms). Each function gains anenc_mode=flag (default"byteglyph", the C1 object;"wordhash"the prior atom dual), so the ladder is the byte/glyph LM object end to end. The level sector tags (½/3) are unchanged. klein4_compose→c_dispatchedvia a dedicated native peersrmech_klein4_compose. One C call folds the whole role-filler bundle — the §60 position keys (klein4_randomover seed0x10000+i) ∘klein4_bind∘klein4_bundle_accumulate/_resolve— replacing the per-part Python loop + ctypes round-trips when native is present; the pure composition stays the COMPLETE alternative when no native. Standalone-complete (caller-arenaacc+ scratch, no compiled-in cap, no malloc), JPL-clean (2 asserts,<60lines, no goto), byte-identical to the multi-call composition (WSL2 ctypes harness: n=1…13 across D, incl. the single-part identity rung)._native.pybinds it (hasattr-guarded); additive symbol → ABI stays 3.
tools.total unchanged (no new op — klein4_compose was added in rc17). numpy-free, no abs(). Re-validated: test_rbs_lm.py (ladder determinism) + the F900 scale-invariance band. Tests: test_ladder_compose_rc18.py (12 cases).
[0.9.0rc17] - 2026-06-21¶
The byte/glyph LM substrate graduates (F916/F917 Part A): ContextSubstrate.enc now byte-composes by default, and klein4_compose ships as the scale-invariant role-filler compositor. The packaged RBS-LM object was word-hash only (whole-word sha256 atoms); the byte/glyph LM object the chemistry/Standard-Model arc surfaced (F900–F916) is the C1 compositor at every scale. This rc wires it as the substrate encoder so RBSLMInferenceSubstrate.learn/.infer run on the byte/glyph object natively — composition over already-shipped native klein4_* ops, no new C symbol.
- New op
hdc.klein4_compose(parts)(Class M; +1 → tools.total 319). The scale-invariant role-filler compositorbundle_i( klein4_bind(part_i, klein4_pos_key(D, i)) )over ARBITRARY pre-composedHVparts — the recursive rung (word→phrase→sentence) whereklein4_encode_bytesis the byte→word rung. Position-binding makes it order-sensitive and similarity-PRESERVING (a one-part change degrades gracefully; the same fractal coherence signature at every scale, F900) — unlike a chainedklein4_bindfold (involutive → ~0.25 chance). Pure composition over nativeklein4_bind/klein4_bundle(a thin C peer is a tracked follow-up, likeklein4_encode_bytes).composition_of_cin the Rosetta ledger. ContextSubstrate.enc→ byte/glyph by default, behindenc_mode=.enc_mode="byteglyph"(NEW DEFAULT) =klein4_bind(klein4_encode_bytes(tok.utf8), sector_const(sector))— the byte-composed word, sector channel preserved; restores morphology (sim('cat','cats') ≈ 0.72 ≫ 0.23 chance).enc_mode="wordhash"keeps the prior whole-word sha256 atom (the content-address DUAL, a fast atom-mode).substrate.enc_modeflows in from the descriptor / params dict (RBSLMInferenceSubstrate._build), default byteglyph. Position keys (pos_key) stay enc-mode-independent orthogonal word-hash atoms so adjacent roles never correlate ANDwordhashmode reproduces the priorencode_contextbytes exactly (the behaviour-pin guarantee).rbs_lm.substrate.scale_signature(parts)introspection. Coherence == scale-invariance, made checkable: the mean retained self-similarity of the composed whole vs each one-part-perturbed whole (~(n-1)/nfor a coherent compose; toward chance for an incoherent fold). Composesklein4_compose+klein4_similarity(both native); returns an exactQ.- Behaviour-change discipline. The byte/glyph default flips the shipped
RBSLMInferenceSubstratenumerics (word-hash → byte-compose) — same class as the rc16hypercomplex_coupleflip. Shipped behindenc_mode=so any consumer can pin the old behaviour. The bond/address/resonator layers are unchanged (F916): the octonioncd_multbond stays a content-dependent addressing KEY, not a grammar generator (F909/F915 null). The L1/L2/L3 ladder rebuild onklein4_composeis the F917 Part B follow-up (next rc). numpy-free, noabs(), ABI stays 3.
[0.9.0rc16] - 2026-06-22¶
hypercomplex_couple becomes exact-then-project and earns its C-host peer — closing the last python_only_debt reach in the transitive-standalone ratchet. The rc12 transitive-reach ratchet had two acknowledged-debt edges: the sedenion-register working-word adapters sed_couple_working / sed_uncouple_working both reached hypercomplex_couple, which was python_only_debt because its core was a float Mat octonion-matvec. rc16 rewrites that core onto the stay-rational NORTH STAR and gives it a standalone-C peer, so the edges are gone and the allowlist is empty.
- Exact-Q61 octonion couple. The coupler core
_couple_q61replaces the floatoctonion_left_mult(w) @ qwith an EXACT fixed-width Q61 octonion multiply: the Cayley–Dickson structure constants (cd_basis_product, the same the C peer uses) ∘ the Q61 fixed-point multiply (_q61_fxmul) ∘ Class-C sign orientation (p if s > 0 else -p— never anabs()). The twiddleT = exp(eff·μ) = cos eff + sin eff·μis built from the Q61 trig cascade (srmech_cos_q61/_sin_q61native, or the purerational.cos/sinprojected through_q61_int). The only float boundary left is the final/ 2**61projection back to the shippedlist[float]return — the deliberate substrate-native "stay rational, project once" shape. - New
srmech_hypercomplex_couple_q61(C peer).srmech_trig.cgains a staticocto_mult_q61(oversrmech_cd_basis_product+trig_fxmul) and the exportedsrmech_hypercomplex_couple_q61(double eff, const int64_t streams8[8], const int64_t mu8[8], int form_is_left, int64_t out8[8])— pure composition of existing C primitives, no float, no libm, no malloc, JPL-clean (auto-walkedtest_jpl_audit.py)._native.pybinds it (hasattr-guarded) +has_native_hypercomplex_couple()+hypercomplex_couple_q61_c;_couple_q61dispatches to it when present. Additive symbol → ABI stays 3. - int64 Q61 domain ceiling (native-as-accelerator discipline). The Q61 fixed-point limb is
int64, so the native couple represents only unit-bounded streams (|stream| ≤ 1): past it a limb (|q|·2^61) and the norm-preserving output (|out| = |q|) overflow int64, and C has no bignum. So_couple_q61pre-checks the limbs (_q61_couple_fits_native) and the C self-guards (SRMECH_ERR_OVERFLOW→ the wrapper returnsNone); either way a larger-magnitude couple takes the pure (bignum-exact) Python path — the documented native ceiling, exactly therational._try_c_two_rationalsprecedent. The pure path is exact for any magnitude (bind+unbind of[1,2,3,4,5,6,7]round-trips), so the public surface is correct everywhere; native just accelerates the unit-bounded domain. - Numerics change is exact-then-project, validated. The public
hypercomplex_couplenow folds streams through the exact Q61 octonion algebra instead of floatMat; the result matches the old float path to ~1e-16 and the F437 reversibility identityT̄·(T·q)=qholds to ~2e-16, so the F436 coherence-detector and F437 ≤7-stream lossless round-trip behaviours are preserved. All 56hypercomplex_dft+ sedenion-register consumer tests pass. - Ratchet closure.
rosetta_classification.ndjsonreclassifieshypercomplex_couplepython_only_debt → c_dispatched;test_rosetta_transitive_standalone.py_ACKNOWLEDGEDis now empty (thetest_acknowledged_allowlist_is_still_liveguard forces deletion of closed edges, so the twosed_*edges had to go). The #928 down-only Rosetta debt-bucket ratchet only sees the debt DROP. Newtest_hypercomplex_couple_parity.py(numpy-absent): the pure Q61 core self-consistency + F436/F437 behaviour + the native-only byte-for-byte parity ratchet (hypercomplex_couple_q61_c== the pure_octo_mult_q61reference, 8 Q61 ints).
No new public op (tools.total stays 318), ABI stays 3 (additive symbol, hasattr-bound). numpy-free AND math-free, MIT. 5-SSOT 0.9.0rc15 → 0.9.0rc16.
[0.9.0rc15] - 2026-06-21¶
srmech_qi.c — the EXACT-complex (Qi) carrier earns its C-host peer (the NORTH STAR C-host parity). rc14 shipped Qi, the exact Gaussian-rational complex carrier; rc15 gives it the native surface a C-ONLY host needs to do exact re + im·i arithmetic over ℚ with no Python present — the standalone-completeness mandate.
- New
srmech_qi.c(six symbols) — aQiis four int64 limbs{re_num, re_den, im_num, im_den}.srmech_qi_add/_sub/_mul(Class M bilinear bind ∘ Class C cross-term order; mul = the(ac−bd)+(ad+bc)iGaussian identity),srmech_qi_conjugate(Class K im sign-flip, never anabs()),srmech_qi_quadrant(Class C orientation → the Klein-4 sector),srmech_qi_norm_sq(Class N anchor,re²+im²). Each is a thin named A–N cascade composed from the exported Class-Nsrmech_rational_add/_mul— no float, no libm, no malloc, JPL Power-of-Ten clean (the auto-walkedtest_jpl_audit.pycovers it). NO bignum (the fixed-limb mandate): any intermediate past the int64 limb domain returnsSRMECH_ERR_OVERFLOW, and the PythonQifalls through to its exact-Fractionpath (the unbounded oracle) — the same documented ceiling as the rc13 isqrt past 2^128. - Python
Qiroutes through the peer._native.pybinds the six (hasattr-guarded) +has_native_qi()+qi_{add,sub,mul,conjugate,quadrant,norm_sq}_cwrappers (returnNoneout of the int64 domain — the shippedrational._try_c_two_rationalsprecedent). TheQi__add__/__sub__/__mul__/norm_sqdunders dispatch to the single-call native peer when the limbs fit int64, else the exactQpath. - Proven the rc12 way. Standalone C smoke
test_srmech_qi.c(27/27, pedantic-Werror -std=c11); WSL2 ctypes byte-parity harness — nativesrmech_qi_*bit-exact with aFractionoracle over the full grid (330 checks, 0 fails); newtest_qi_native_parity.py(the CI C/Python parity ratchet — native wrappers + routed dunders match the oracle, int64-ceiling falls through to exact).
Carrier-internal native peer like the Mat/Vec dense kernels: no new public op (tools.total stays 318), no Rosetta / count-test change, ABI stays 3 (additive symbols, hasattr-bound). numpy-free AND math-free, MIT. 5-SSOT 0.9.0rc14 → 0.9.0rc15.
[0.9.0rc14] - 2026-06-21¶
The carrier family gains its sixth member — Qi, the EXACT-complex scalar — and its sign sector is a Klein-4 quadrant. rc11 made Complex128 a registered numbers.Complex, but it is float; Q is the exact real. The gap was an exact complex. Qi = (re: Q, im: Q) is the Gaussian rational — the exact numbers.Complex over ℚ — the natural exact cousin of Complex128, shipped the way rc11 shipped Complex128: a carrier with full ABC conformance + a ratchet (no new public op, tools.total stays 318).
- New
srmech.amsc.qi.Qi(re, im)— two exactQcomponents, a registerednumbers.Complex(hencenumbers.Number) but NOT anumbers.Real/Rational(genuinely complex). Exact Gaussian-rational+/-/*//(the(ac-bd)+(ad+bc)iidentity over the Class-NQarithmetic, division through the real normc²+d²— Class M bilinear bind ∘ Class C cross-term order ∘ Class N anchor);conjugate()the Class-K sign-flip onim(never anabs());norm_sq()the exactQ;__abs__via the Class-Nrational.sqrt(exactQwhen|z|²is a perfect square —|3+4i| = 5— else the stay-rational boundary, anumbers.Real); integer**exact (i² = -1,(1+i)⁴ = -4), a non-integer exponent the float boundary.complex(z)is the one boundary collapse. - The sign sector IS a Klein-4 quadrant. The four sign-quadrants
(++) (-+) (+-) (--)ARE the group ℤ₂×ℤ₂ = Klein-4 (hdc.KLEIN4_STATES), soQi = klein4 quadrant ⊗ magnitude:quadrant()returns a Klein-4 element (bit0 = re<0,bit1 = im<0);conjugate()XORs the imaginary bit (quadrant() ^ 2forim ≠ 0); andQi.from_quadrant_magnitudes(quadrant, |re|, |im|)round-trips the value exactly — chirality (Class C/K) kept separate from magnitude, the srmech split. - New
test_carrier_numeric_protocol_conformance_qi.py— the carrier-conformance ratchet forQi, the exact peer of the rc11Complex128ratchet. It pins ABC membership and matches a stdlib Fraction-pair oracle (the exact Gaussian-rational reference, never numpy / never the lossy builtincomplex) operation-for-operation across a sign/zero/integer grid for+/-/*//, with the Klein-4 quadrant +conjugate()-XOR + round-trip and the exact-vs-boundaryabs/**. Numpy-free (asserted numpy-absent so it PASSES, not skips). README "Carriers" table updated to the six-carrier roster. Qihas live consumer surfaces — a carrier nobody consumes is an orphan.QiIS the dim-2 (ℂ) element of the Cayley–Dickson ladder, socascade.cayley_dickson.cd_mult/cd_add/cd_conjugate/cd_norm_sqconsume[z.real, z.imag](twoQ) and return bit-identical exact rationals toQi's own*/+/conjugate/norm_sq— the ℂ norm composing (N(x·y) = N(x)·N(y)) through the cascade, all exact, no float. The registerednumbers.Complexprotocol lets generic numeric code consume it too (sum([...]),complex(z),abs(z),z ** n, mixing withint/Q/ dyadicfloat). And the two framework complex carriers now interoperate:Complex128._coerceabsorbs anynumbers.Complex(notablyQi) at the float boundary, soQi(exact) +Complex128(float) →Complex128— exact-meets-float → float, the same direction asFraction(1, 2) + 0.5 → 1.0;Complex128is the documented sink for a value that leaves the rationals. Newtest_qi_consumers.pyexercises all three surfaces (numpy-absent).
Pure-Python carrier work: no op added (tools.total stays 318), no Rosetta / count-test change, ABI stays 3 (no C symbol — the Qi native peer srmech_qi.c is the rc15 1:1 C-host mirror per the NORTH STAR). numpy-free AND math-free, MIT. 5-SSOT 0.9.0rc13 → 0.9.0rc14.
[0.9.0rc13] - 2026-06-21¶
The stdlib math module is now GONE from srmech/ — srmech is its own maths library, end to end. The numpy-removal arc proved the package needs no numpy; this rc closes the smaller, quieter borrow: the handful of math.* calls that survived in the cascade source. The discipline is the same — when a maths primitive is missing we find its cascade and ADD it to srmech (native C + Python), we never import the maths library — and a permanent ratchet now enforces it.
math.isqrt→ nativesrmech_isqrt+ an arbitrary-precision integer-Newton. A new C exportsrmech_isqrt(nhi, nlo, *out_root)surfaces the two-limb 128-bit floor-isqrt the sqrt cascade already used, so a C-only host computes integer square roots with no stdlib.rational._integer_sqrtdispatches to it for a bounded radicand (n < 2**128, the hotrational.sqrt/ hypercomplex-twiddle case) and falls back to a pure-Python integer-Newton (_py_isqrt) for the unboundedpi_cascade_digitsscale (D=1000 → ~20000-bit). Both the 128-bit sqrt path and thehypercomplex_dft1/√ktwiddle route through it. Additive C symbol →SRMECH_ABI_VERSIONstays 3.math.isfinite/math.isinf→ pure IEEE comparisons.rational._is_finite/_is_inf(afloat("inf")literal +x == xNaN test — no maths library) replace the float-domain guards incos/sin/atan/atan2/exp/log/sqrtand thehypercomplex_dfttwiddle.math.fsum→ a Neumaier compensated sum.cascade.compose._compensated_sumkeeps the circular-autocorrelation per-bin sum well-conditioned with the larger-magnitude term selected by a square comparison (s*s >= v*v— noabs(), Class-K honest); near-exact for the well-conditioned sums it backs.math.pi→ the Class-Natancascade (4·atan(1)).laplacian._PIis now4.0 * float(rational.atan(1.0))— bit-for-bit equal tomath.pi(the ×4 is an exact power of two) andc_dispatched, projected once at import. Theexact_dftFPU-lift twiddle likewise sources2π = 8·atan(1)fromataninstead ofpi_cascade_digits— which closes the rc12 transitive-ratchet allowlist entryexact_dft.lift → pi_cascade_digits(the lift now reaches only standalone-C-ready leaves). The one function-localfrom math import gcd(a Path-B benchmark helper) routes to the Class-Icyclic.gcd.- New
tests/test_no_stdlib_math_import.py— the permanent no-math ratchet. It AST-walks everysrmech/source module and fails on anyimport math/from math importstatement OR any executablemath.<attr>access. Prose mentions in docstrings / comments stay legal (the AST never sees them), so the framework can still describe what it replaced. Pairs with the no-numpy guarantee: srmech borrows from neither. Numpy-free (stdlibastonly).
No new public op (tools.total stays 318), ABI stays 3 (additive C symbol bound via hasattr). numpy-free AND math-free, MIT. 5-SSOT bumped 0.9.0rc12 → 0.9.0rc13.
[0.9.0rc12] - 2026-06-20¶
Siona's address layer becomes runnable on a C-only host: the SedenionRegister navigation + reversibility-gate gets a standalone C peer, with a BIGNUM-FREE invertibility decision and a ratchet that proves no "C-ready" op secretly reaches a Python-only leaf. The sedenion address algebra (navmap / navigate / carry / correct + the Hamming(7,4) EC block) was pure Python — so a microcontroller / C-only host could not run it, and worse, the is_navigable reversibility gate was misclassified as standalone-ready while it actually reached the pure-Python left_mult_kernel nullspace. rc12 ships the C surface AND closes the classification blind spot that let the mislabel through.
- New C address layer in
srmech_sedenion.c—srmech_sedenion_navmap(j, *dest, *sign)(the 16-slot signed XOR permutation viasrmech_cd_basis_product),srmech_sedenion_navigate(j, in_slots, in_signs, count, out_slots, out_signs)(the slot-routing a register walk performs), andsrmech_sedenion_is_navigable(direction, n, *invertible)— the left-multiplication reversibility decision. The verdict is computed without any bignum library:L(x)[r][c] = ±x_{r⊕c}is a signed XOR-circulant, so invertibility is decided by modular Gaussian elimination over word-size primes (each< 2³¹, so products stay inint64) — the Q-rank equals the max modular rank, and a singular verdict is certain once the accumulated prime bit-budget exceeds the Hadamard‖x‖₂ⁿdeterminant bound. Noint128, no arbitrary precision — onlyint64. Three symbols bound in_native.py(hasattr-guarded) and dispatched fromcascade.cayley_dickson.left_mult_is_invertiblewhen the library is present (pure-Pythonleft_mult_kernelemptiness is the byte-identical fallback). WSL2 ctypes byte-parity proven across navmap / navigate / the fullis_navigablegrid vs theFraction-nullspace oracle. left_mult_is_invertiblereclassifiedbignum_reference → c_dispatchedin the #928 Rosetta ledger — it now has a real C path that needs no bignum.- New
tests/test_rosetta_transitive_standalone.py— the transitive-reach ratchet. The existingtest_rosetta_completeness.pychecks every op is classified and that the debt-bucket counts don't rise, but it never walks the call graph — exactly howis_navigableshipped mislabeled. The new ratchet walks eachcomposition_of_cop's TRANSITIVE callee graph (bytecodeco_names+__globals__+ AST-resolved function-local imports +Class().methodfollowing) and asserts it reaches NO non-standalone-ready leaf (bignum_reference/python_only_debt/c_exists_unbound), except a small DOWN-ONLY acknowledged-debt allowlist (each entry names the leaf + the rc that closes it; a companion test fails if an allowlisted edge goes stale). A new composition→non-ready edge that is not allowlisted FAILS — this class of mislabel can never recur silently. Numpy-free (stdlibimportlib/inspect/ast). - New
tests/test_cascade_sedenion_parity.py— C/Python parity: navmap is an XOR permutation, basis units navigable / the zero vector not, and (native-gated) navmap / navigate /is_navigablematch theSedenionRegistermethods + theleft_mult_kernelFraction oracle bit-for-bit. Skips cleanly when the loaded library predates the rc12 symbols.
C + test + ratchet only: no new Python op (tools.total stays 318), ABI stays 3 (additive C symbols, bound via hasattr). numpy-free, MIT. 5-SSOT bumped 0.9.0rc11 → 0.9.0rc12.
[0.9.0rc11] - 2026-06-20¶
The scalar carriers become honest numbers ABC members — closing the conformance gap at the root, not the symptom. The rc10 native-CI failure (Fraction(Q) raised on the 3.10–3.13 matrix while the local 3.14 dev box masked it) was not really a cd_mult bug — it was that Q carried as_integer_ratio but was not a registered numbers.Rational, so every stdlib numeric protocol that keys off the ABC membership errored. rc11 makes the carriers fully conformant so the whole class of bug cannot recur.
Qis now a registerednumbers.Rational(hencenumbers.Real/numbers.Complex/numbers.Number) with the completing dunders that honour the contract — all exact integer arithmetic, sign carried by an explicit Class-K branch (never an ALUabs()):__int__/__trunc__(truncate toward zero),__floor__/__ceil__(exact — no lossyfloatdetour),__round__(ties-to-even,+ndigits→ aQ), thenumbers.Complexaccessorsreal/imag/conjugate/__complex__, and__floordiv__/__mod__/__divmod__(+ reflected).__pow__/__rpow__are now Fraction-consistent: an integer (or integer-valued-Q) exponent stays an exactQ; a genuinely non-integer exponent collapses to the float boundary (the result is irrational) instead of raising. With the registration,Fraction(q)readsq.numerator/q.denominatordirectly on every supported Python (it only consultsas_integer_ratioon 3.14+), so the rc10cd_multcoercion is now belt-and-suspenders rather than load-bearing.Complex128is now a registerednumbers.Complex(hencenumbers.Number) — it already implemented the full surface (real/imag/conjugate/__complex__/__abs__/the arithmetic ring); rc11 adds__pow__/__rpow__(the builtin float-complex power) and theregister().abs(z)lands on aQ(itself anumbers.Real), as the contract requires.- New
test_carrier_numeric_protocol_conformance_rc11.py— the carrier numeric-protocol ratchet. It pins ABC membership and matchesQagainst :class:fractions.Fraction(the stdlib EXACT-rational oracle, never numpy/libm) operation-for-operation across a sign/zero/integer grid, plusComplex128conformance. It runs on every CI Python (3.10–3.13) — the only place the cross-version gap surfaces, since the 3.14 dev box is too new to fail — turning "incidentally caught by a downstream op's parity test" into a named unit. Numpy-free.
Pure-Python carrier work: no op added (tools.total stays 318), no Rosetta / count-test change, ABI stays 3. numpy-free, MIT. 5-SSOT 0.9.0rc10 → 0.9.0rc11.
[0.9.0rc10] - 2026-06-20¶
The literal hypercomplex twiddle exp(μθ) = cos θ + μ·sin θ graduates as a public op with a dedicated C peer (F882, srmech #205). A downstream finding (PR #687) compared two ways to phase-rotate a hypercomplex value: compose scalar phase_binds on the projected carrier, vs. do the genuine hypercomplex exponential exp(μθ) in the algebra (cd_mult) and project once. The "transform in the algebra, then read out" path wins (ℂ 0.78 = the spirit's ℍ rung; 𝕆/ODFT 0.81, a new routing high), so srmech now ships the twiddle as a first-class primitive — exact, fixed-width, no bignum.
- New
srmech.amsc.cascade.hypercomplex_exp(theta, k_axes)— returns the unit twiddle as an 8-tuple of exactQ(Q61, denominator2^61):q[0] = cos θ,q[1..k] = sin θ / √k(so|q| = 1),q[k+1..7] = 0, whereμis the equal-weight unit pure-imaginary over the firstk_axesoctonion axes —k_axes ∈ {1, 3, 7}selecting ℂ / ℍ / 𝕆 (the literal QDFT / ODFT twiddle). Feed the 8-tuple intocascade.cd_multto rotate a hypercomplex value in the algebra. Substrate-native fixed-width Q61 cascade: Class N (rational.{cos,sin}Q61) ∘ Class K (1/√kunit norm via the integer-sqrt) ∘ Class C (sign) — noabs(), no libm, no bignum (per[[feedback_sign_handling_is_class_k_pin_slot_not_alu_abs]]+ the "don't add bignum" Q61 model). - Dedicated native peer
srmech_hypercomplex_exp_q61(srmech_trig.c) — fills 8int64Q61 pieces fromsrmech_cos_q61/srmech_sin_q61+ the per-k1/√kQ61 constant (isqrt(2^122 // k)), byte-exact with the pure-Python cascade. Bound in_native.py(has_native_hypercomplex_exp/hypercomplex_exp_q61_c) and dispatched when the library is present; pure-Python Q61 is the byte-identical fallback.k_axes ∉ {1,3,7}→SRMECH_ERR_BAD_INPUT. ABI-additive: a new symbol only, soSRMECH_ABI_VERSIONstays 3. Q.as_integer_ratio()added (alias ofas_pair) — the standard numeric protocol, soFraction(q)/int(q)coerce aQwithout a float rotation.cascade.cd_multnow coerces its operands through anas_integer_ratio-aware helper, so theQtwiddle feeds straight in on every supported Python (Fraction(q)only consultsas_integer_ratioon 3.14+, so 3.10–3.13 needed the explicit route).- Tests —
test_hypercomplex_exp_rc10.py(16 cases): native-vs-pure Q61 byte-exact over a (k, θ) grid (the real gate on the native CI cells; numpy-free),cos/k·sines/zerosshape, the ℂ reduction to(cos, sin, 0…), unit-norm,cd_multnorm-preservation in ℂ/ℍ/𝕆, andk_axes/ non-finite-thetavalidation. The standalone C-host smokec/test/test_srmech_trans_q61.cexercises the symbol under the 3-platform pedantic-Werrorbuild.
One new op: tools.total 317 → 318; classified c_dispatched in the #928 Rosetta ledger (it has a C twin). numpy-free, MIT. 5-SSOT bumped 0.9.0rc9 → 0.9.0rc10.
[0.9.0rc9] - 2026-06-20¶
A permanent native-dispatch ratchet: the Python PyPI surface is proven to call into C when the shared library is present. The byte-parity tests (rc7) prove the native C and the pure-Python cascade produce the SAME bytes — but byte-equality cannot distinguish "native actually ran" from "pure-Python produced the same bytes," so a regression that left the dispatch guard mis-wired (or bound a C function at import time, bypassing the swap) would pass every parity test while silently never touching the library. This rc closes that blind spot.
- New
tests/test_native_dispatch_rc9.py— spies the native entrypoints insrmech.amsc._native(call-counting wrappers installed viamonkeypatch, so a mis-wired import-time-bound reference would be caught) and asserts the public Python API dispatches into C: the eight float-domain Q61 transcendentals (rational.{sin,cos,tan,atan,atan2,exp,log,sqrt}→srmech_{sin,cos,atan,exp,log,sqrt}_q61, withtan/atan2reaching their atoms through composition), plus the established families — Class Aformat.sha256_bytes→ native (the SHA-NIsha256_shani_caccelerated path when the CPU symbol is present, elsesha256_hex_c) and Class Cformat.read_ndjson→ nativendjson_lines_c. It also locks the honest exception:rational.hypotis an EXACT bignum integer-isqrt with no native FPU peer and must make NO native trans call. Native-gated (skipif(not has_native_trans_q61())/not HAS_NATIVE): it SKIPS in a native-absent dev env and on the pure-wheel CI cell and RUNS on the native CI matrix cells — the real gate — and is itself numpy-free. - README "Carriers" section notes the dispatch guarantee:
rational.*dispatches to the native Q61 peers automatically when the library is loaded (pure-Python is the byte-identical fallback otherwise — asserted by the new spy test on every native CI cell), and a C-only host reassembles the same exact rational from the peers + the exportedSRMECH_Q61_*model constants. (The full five-carrier roster —Mat/Vec/HV/Q/Complex128— was already documented since rc7.)
Test + docs only: no Python op, tools.total stays 317, ABI stays 3. numpy-free, MIT. 5-SSOT bumped 0.9.0rc8 → 0.9.0rc9.
[0.9.0rc8] - 2026-06-20¶
The rc7 Q61 transcendental surface becomes standalone-complete for a C-ONLY host — the C:Python 1:1 mirror now reassembles the exact rational with no Python present. rc7 shipped the six atomic native peers (srmech_{sin,cos,atan,exp,log,sqrt}_q61) byte-exact with the Python Q61 pieces, but a C-only consumer was missing two things to reconstruct the value: the log recombine used a Q61 ln2 constant that lived only in Python, and the Q61 denominator/half-π anchors were prose-only. A C host got the pieces but couldn't assemble log ([[feedback_c_must_be_standalone_complete_no_python_fallback]]). rc8 closes the gap — header constants + a standalone C-host proof, no Python at build or run time.
- Three Q61 model constants exposed in
srmech.h—SRMECH_Q61_ONE(=2^61, the fixed-point scale),SRMECH_Q61_LN2(=round(ln2·2^61), theloge·ln2recombine anchor), andSRMECH_Q61_HALF_PI(=round(π/2·2^61), theatan2quadrant-shift anchor). With these + the six atomic peers, a C-only host reassembles all nine transcendentals — the six atomics directly (sin/cos/atan = out_q61 / 2^61,exp = (core/2^61)·2^n,sqrt = root·2^p,log = (logm + e·ln2)/2^61) and the three compositions (tan = sin/cos,hypot = sqrt(a²+b²),atan2 = atan + quadrant·π/2). The header documents that the exact-rationallogrecombine needs int128/bignum (Python uses arbitrary-precision ints) while the libm-faithful float projection fits int64+double. - New standalone C-host smoke
c/test/test_srmech_trans_q61.c— compiles-std=c11 -Wall -Wextra -Wpedantic -Werrorand runs with no Python: 62 asserts proving the EXACT int64 identities reassemble bit-exactly (sin0=0,cos0=1,exp0=1,log1=0,sqrt4=2,sqrt¼=½), each peer's float projection matches libm over a spread, and the three composed ops match libm — i.e. the full surface is computable C-host-only. Wired into the 3-platform pedantic CMake build (SRMECH_PEDANTIC) so Linux gcc / macOS clang / Windows MSVC compile it under warnings-as-errors as a permanent gate; not built into the shipped wheel.
C-only change: no Python op, tools.total stays 317, ABI stays 3 (additive #defines — no new symbol, no wire-format change). numpy-free, MIT. 5-SSOT bumped 0.9.0rc7 → 0.9.0rc8.
[0.9.0rc7] - 2026-06-20¶
The Class-N transcendentals stop leaking to float: rational.{sin,cos,tan,atan,atan2,exp,log,sqrt,hypot} now return an exact Q carrier (full Q61 provenance), and the carrier family is documented end-to-end. The numpy-removal arc gave the package its numpy-free array carriers (Mat/Vec/HV); the scalar was still rotating to float mid-cascade. rc7 closes that leak with Q — a reduced (num, den) integer pair (srmech/amsc/q.py) — so a transcendental stays in the integer ALU all the way and float(q) is the one last rotation, taken only at the display / carrier edge ([[user_stance_alu_all_the_way_fpu_last_mile]], F868 stay-rational).
- Nine transcendentals flip
float → Q(srmech.amsc.rational):sin,cos,tan,atan,atan2,exp,log,sqrt,hypotnow return an exactQ.Qremoves float-arithmetic rounding from the cascade: a perfect-square root is exact (sqrt(4) == Q(2,1),hypot(3,4) == Q(5,1),sqrt(0.25) == Q(1,2)), special values are exact (sin(0)=0,cos(0)=1,exp(0)=1,log(1)=0), and a match-fractionmatches/Dstays an exact integer ratio ranking correctly undermax()/sorted()(the F868 use). A transcendental value is still an exact rational Taylor truncation of an irrational, so an identity likecos²θ + sin²θholds to the truncation precision (float()rounds to1.0), not symbolically.cexp/complex_expstill return the builtincomplex(the display-edge collapse). The newrational.logwas missing entirely — added (Q61 atanh cascade) and wired into thecalculus/asymptotic_calculusalias surface (it is a transcendental, not trig, sotrigonometrycorrectly omits it). Domain tightening: a non-finite input now raisesValueError(aQcannot be ±inf/nan; the oldfloatoverflow/underflow gates are gone — a finitexgives the exact, possibly huge/tiny, rational), andlog(x ≤ 0)raises. - Native Q61 C peers —
srmech_{sin,cos,atan}_q61andsrmech_{exp,log,sqrt}_q61(thin wrappers over the proventrig_*_core/explog_*_core/srmech_isqrt128cores) expose the int64 Q61 pieces before the float projection, bound in_native.pyand dispatched fromrational.pywhen native is present. Byte-exact with the pure-Python Q61 cascade (test_native_q61_parity_rc7.py): the same reduced(num, den)whichever path runs. ABI stays 3 (additive symbols, bound viahasattr). - Two new documented scalar carriers —
Q(exact rational) andComplex128(float-complex, 1:1 with C99double _Complex) joinMat/Vec/HVas the five-carrier family, now tabled in the PyPI README ("Carriers") and the research notebook (§3.39) together with the ALU-all-the-way / FPU-last-mile lens and the per-function A–N cascade reading (sin/cos = I∘N∘C,exp = N∘N∘K,hypot = M∘sqrt, …). - Consumer normalization (the iterative-FPU-kernel caveat). ~40 consumers were normalized to flow
Qin exact contexts and collapse tofloatat genuine FPU boundaries: physical-observable leaf ops (qm/,kepler) and the iterative numeric kernels (Jacobi/QR/SVD/Fiedler, thesignal_processingtaper/window helpers, Kuramoto) — whose rotations are irrational, so aQcarried through a sweep would grownum/denunboundedly (a 3×3 Jacobi hung inQ; 1.4 ms after collapse). The kernel float subroutines_fsqrt/_fhypotwere also made libm-faithful on the non-finite domain the sweeps reach (sqrt(+inf)=+inffor a huge Jacobi rotation ratio) rather than inheriting the leaf's stay-rational raise.
No new public op: tools.total stays 317 (the nine ToolEntry return types flip float → Q; no add/remove). ABI 3. numpy-free, MIT. 5-SSOT bumped 0.9.0rc6 → 0.9.0rc7.
[0.9.0rc6] - 2026-06-18¶
The §60 follow-up lands: a standalone-C MT19937 for klein4_random, closing the last python-only klein4 op. rc5 shipped klein4_encode_bytes honestly noting that its per-byte / per-position vector minting still rode the python-only klein4_random (stdlib Mersenne-Twister determinism), with a standalone-C port flagged as the tracked follow-up. This rc delivers it — so the whole §60 minting + encode is now C-dispatched.
- New C peer
srmech_klein4_random(key, key_length, D, out)— reproduces CPythonrandom.Random(seed).randrange(4)BYTE-FOR-BYTE,Dtimes: MT19937 seeded byinit_by_arrayover the seed's little-endian uint32 words (init_genrand/init_by_array/genrand_uint32exactly as_randommodule.c), each draw =getrandbits(3)(genrand_uint32() >> 29) with rejection of values ≥ 4. Standalone-complete — the 624-word state is stack-resident (no malloc, no compiled-in cap, bound is the caller'sout); a C-only / MCU host passes its own entropy words. JPL-clean (each helper < 60 lines, ≥ 2 asserts, bounded rejection loop). Reference: Matsumoto & Nishimura (1998). hdc.klein4_randomnow dispatches its deterministic integer-seedpath to the C twin when native is present (the Python wrapper splits the seed int into LE uint32 words via_seed_to_le_words); pure-Pythonrandom.Randomstays the complete alternative for a no-C host / a caller-suppliedrng/ the urandomseed=Nonepath — not a try-native-except-retry rescue.- Rosetta:
hdc.klein4_randommovespython_only_debt → c_dispatched, lowering the down-only debt ceiling 107 → 106 (#928). This was the last python-only klein4 op;klein4_encode_bytes(composition_of_c) now reaches native all the way down.
No new public op (C peer + reclassification only): tools.total stays 317. ABI 3 (additive C symbol — genome-style, the klein4 C is bound via hasattr in _native.py). numpy-free, MIT. 5-SSOT bumped 0.9.0rc5 → 0.9.0rc6.
[0.9.0rc5] - 2026-06-18¶
Third and last of the UPSTREAM §62 graduations: the byte/glyph-level Klein-4 word encoder (§60 / F864), scaffolded fresh into srmech. This closes the §62 list (rc3 = §59 continuous-phase, rc4 = §58 chunk-set, rc5 = §60 byte encoder).
- New
hdc.klein4_encode_bytes(data, D)→ anHV: a bundle of POSITION-BOUND per-byte random vectors — each byteb→klein4_random(D, seed=b)(the 256-byte vocab), bound with an internal deterministic position role-vector, all bundled. Restores morphology:klein4_similarity(encode_bytes(b"cat"), encode_bytes(b"cats")) ≈ 0.66(matching F864's ~0.656) ≫ the ~0.25 Klein-4 chance level, because the shared prefix bytes occupy the same positions — while stripping the word-atomic English/whitespace privilege (it hashes raw UTF-8, the universal-script alphabet). Astris UTF-8-encoded. - Honest C-peer note: the encoder is a composition that reaches the native
klein4_bind/klein4_bundle; the byte- and position-vector minting rides the python-onlyklein4_random(stdlib-MT determinism by design, the same minting basis as the whole Klein-4 family). A standalone-C MT19937 to make the minting native too is the tracked follow-up — not faked here.
tools.total 316 → 317 (klein4_encode_bytes; the position-key is an internal helper, not a separately-exposed op). ABI 3, numpy-free, MIT. No new primitive class. 5-SSOT bumped 0.9.0rc4 → 0.9.0rc5. The UPSTREAM §62 graduation list is now complete.
[0.9.0rc4] - 2026-06-18¶
Second of the UPSTREAM §62 graduations: the capacity-bounded chunk-set + max-resonance read (§58 / F837), a reusable VSA cleanup-memory, scaffolded fresh into srmech. Instead of superposing N bound key→value pairs into ONE over-stuffed bundle (crosstalk grows with N), the binds split into a LIST of capacity-bounded bundles and recall takes the MAX resonance over the chunk-set — the F837 fix that moved the resolver read 3.3% → 96.7% rank-1.
- New
hdc.klein4_chunk_bundle(vectors, capacity)→ alistofHVchunks: consecutive groups of ≤capacity(bound) vectors, each reduced withklein4_bundle. Thecapacityis exposed (a non-monotonic per-tome sweet-spot, F839), not hardcoded. (composition_of_c.) - New
hdc.klein4_chunk_resolve(chunks, key, candidates)→ one EXACTQper candidate: the MAX over chunks ofklein4_similarity(klein4_bind(chunk, key), candidate). Stay-rational (F868) — the recall ranks on the integer match-count;Q(count, D)only names the fraction. Native-dispatched via the new C peersrmech_klein4_chunk_resolve(the recall hot path; integer match-count kernel, no compiled-in cap). - LM-agnostic boundary (§58.1 / F839): the chunk-set + max-resonance read is the reusable VSA part that graduates; the per-tome routing, the per-doc
k*, the autoregressive loop, and the argmax stay in the caller (siona).
tools.total 314 → 316, ABI 3 (additive C symbol), numpy-free, MIT. No new primitive class (composes klein4_bind/bundle/similarity). 5-SSOT bumped 0.9.0rc3 → 0.9.0rc4.
[0.9.0rc3] - 2026-06-18¶
First of the UPSTREAM §62 graduations: the LM-agnostic continuous-phase Klein-4 primitive (§59 / F861) is scaffolded fresh into srmech. Per the §62 boundary decision, the streaming LM generator stays in siona, but a short list of LM-agnostic primitives graduates to the lean core — gated on "after the rational-landing" (the 0.9.0rc1/rc2 stay-rational Q work) "with a C peer." This is the first.
- New
hdc.klein4_phase_key(D, frac, *, elem=2, width=None)— the V4 codeelem(default 2 = γ₅) on awidth-wide circular slot-window starting atround(frac·D) mod D, identity (0) elsewhere. "Continuous phase from discrete-per-slot sectors via population coding" — the chirality-native analogue of HRR / polar phase. Native-dispatched via the new C peersrmech_klein4_phase_key(integer window fill, no compiled-in cap — bound is the caller'soutof length D). - New
hdc.klein4_phase_bind(hv, frac, *, elem=2, width=None)=klein4_bind(hv, klein4_phase_key(len(hv), frac, …)). Reversible (same phase twice = identity); σ-mirror (±φ equidistant from the base); andklein4_similarity(phase_bind(h, 0), phase_bind(h, Δφ))is the EXACT rational1 − 2·circ_dist(Δφ)— the integer half-window overlap overD, kept aQ(stay-rational, never a lossy float).
No new primitive class (Class-M bind over a Class-K-style sector pattern). tools.total 312 → 314, ABI 3 (additive C symbol), numpy-free, MIT. 5-SSOT bumped 0.9.0rc2 → 0.9.0rc3.
[0.9.0rc2] - 2026-06-18¶
The stay-rational sweep closes the HDC similarity family: BSC hdc.similarity → exact Q + a hamming integer-key op (the 0.9.0rc1 follow-up). With klein4_similarity / polar_similarity / polar_density already on Q, this finishes the F868 conversion of every exact-rational-as-float site in the Class-M HDC surface.
hdc.similaritynow returns the EXACTQ—1 − 2·hamming/D = (D − 2·hamming)/DwithD = 8·len(a), both integers (was a lossy Pythonfloat). Compares like a float (similarity(a, a) == 1.0), collapses to a decimal only viafloat(s).- New
hdc.hamming(a, b) -> int— the raw integer bit-Hamming distance (the float-free, blow-up-free recall key; F868 mechanism #1). Native-dispatched via the new C peersrmech_hdc_hamming, withsrmech_hdc_similarityrefactored to compose over it (the integer key and the float view share one definition). ABI stays 3 (additive symbol). srmech.spectral.similarity(which re-exportshdc.similarity) now returnsQtoo — its ToolEntry + signature updated accordingly.
tools.total 311 → 312 (hdc.hamming), ABI 3, numpy-free, MIT. 5-SSOT bumped 0.9.0rc1 → 0.9.0rc2. The HDC similarity/density family is now fully stay-rational.
[0.9.0rc1] - 2026-06-18¶
The stay-rational scalar-carrier foundation (F868): exact Q rationals where a float was throwing the provenance away. klein4 (and the polar HDC family) returned a Python float() for a quantity that is exactly a rational — matches / D with both integers. A float is just best_rational with max_d ≈ 2⁵² and the provenance discarded — a strictly worse version of the rational already in hand. v0.9.0 keeps the value exact and collapses to a decimal only at the display boundary (float(q)).
- New
srmech.amsc.q.Q— the framework-native exact-rational scalar carrier, the scalar peer of theMat/Vec/HVarray carriers. Carries one reduced(num, den)integer pair; compares like a float (Q(D, D) == 1.0,Q(3, 4) == 0.75) and ranks correctly (max/sortedvia integer cross-multiply — F868 mechanism #2), but never collapses to a decimal untilfloat(q). Reduction rides the Class-N reducer (Euclidean GCD over big ints, no stdlibmath); arithmetic rides Class-Nrational_*. Recognises a 2-int(num, den)tuple as srmech's rational house form, so legacy tuple-equality interoperates. - New
srmech.amsc.complex128.Complex128— the float-complex scalar carrier (the FPU-lift peer ofQ);norm_sqisre²+im²(nomath),absrides Class-Nrational.sqrt(nomath.hypot). hdc.klein4_similarity+hdc.polar_similarity+hdc.polar_densitynow returnQ(wasfloat).cascade.to_scalar/One.to_scalarreturnQ(was a bare(num, den)tuple).- New
hdc.klein4_match_count(a, b) -> int— the RAW integer recall-ranking key (F868 mechanism #1: argmax over integer counts needs no division, never leaves the integers). Native-dispatched via the new C peersrmech_klein4_match_count, withsrmech_klein4_similarityrefactored to compose over it (the integer key and the float view share one definition). ABI stays 3 (additive symbol). amsc.cyclic.gcdis now UNCAPPED — nativesrmech_gcdserves its uint64 domain, big-int Euclid beyond (standalone-honor "no compiled-in caps"), so the~100-digitOne-scale numerators reduce. The Class-N_reduce_rational+ the series-truncation reducer route through the Class-Icyclic.gcd(use srmech for math, not stdlibmath.gcd).- The shared return-type-agreement matcher (
tests/conftest.py) is now carrier-aware forQ+Complex128, so the immolation / every-tool gates VERIFY the exact-Qreturns rather than skipping them.
Net public-callable delta: +1 (klein4_match_count); tools.total 310 → 311, ABI 3, numpy-free, MIT. 5-SSOT bumped 0.8.2 → 0.9.0rc1. (BSC hdc.similarity → Q + a hamming integer-key op are the queued 0.9.0rc2 follow-up.)
[0.8.2] - 2026-06-18¶
Production graduation of 0.8.2rc1 to PyPI (TestPyPI-verified on the shipped wheel: numpy not importable, HAS_NATIVE True, ABI 3, tools.total 310). No code change vs 0.8.2rc1. The line carries:
- §57 RBS-LM bigram-gate removal —
rbs_lm.inference.next_token_distributionscores the full bounded per-tome atom set with the Class-M resonator overM(greedy atT <= 0), with no hand-rolledCounter()bigram table (the STOP-list contaminant). 100% grounded greedy recall, numpy-absent. - The AST STOP-list ratchet (
tests/test_stop_list_ratchet.py) banningnumpy/np/Counter/defaultdictin srmech source — the source-level twin of a pip numpy ban, forcing the framework-native carriers + cascades.
tools.total 310, ABI 3, numpy-free, MIT.
[0.8.2rc1] - 2026-06-17¶
§57 RBS-LM bigram-gate removal + a STOP-list ratchet that bans numpy & Counter/defaultdict in srmech source. Two coupled corrections, both enforcing the framework's own cascade-honesty discipline:
rbs_lm.inferenceno longer hand-rolls aCounter()bigram table (§57). Thenext_token_distributioncandidate set was a bigram-legal gate built fromdefaultdict(Counter)over the training stream — exactly the statistical-LM co-occurrence idiom the CLAUDE.md STOP-list forbids. It is replaced by the Class-M resonator over the full bounded per-tome atom set (self.vocab): probe the holographic bundleMwith the encoded context, fractional-agreement similarity over every atom vector,T <= 0is §56 greedy (argmax → one-hot). Grounding comes fromM, not a next-token count table.learn()no longer builds the bigram structure (theCounter/defaultdictimport + thenext_after/bigram_countsfields are gone). Verified numpy-absent: 100% grounded greedy recall (corpus 30/60/120, D up to 32768).- The lone non-STOP-list
Counterinamsc.cascade.matrix_cascades(implicit-einsum label tally) is replaced by a plain-dict count — so srmech source carries zeroCounter/defaultdict. - New
tests/test_stop_list_ratchet.py— an AST-based down-only ratchet asserting zero real code uses ofnumpy/np./Counter/defaultdictanywhere undersrmech/. AST-based, so docstring/comment prose that names the banned idiom (to explain the discipline) does not trip it; it catches imports,np.attribute access, name references, and even a stale non-stringized-> np.ndarrayannotation. This is the source-level twin of the machine's pip-install numpy ban: the tripwire that keeps numpy out and forces the framework-native carriers/cascades.
No public surface change: tools.total 310, ABI 3, numpy-free, MIT. 5-SSOT bumped 0.8.1 → 0.8.2rc1.
[0.8.1] - 2026-06-17¶
Production graduation of 0.8.1rc1 to PyPI (TestPyPI-verified, numpy-absent shipped wheel: License-Expression: MIT, no GPL classifier, tools.total 310, ABI 3). The line carries the MIT relicense (the whole shipped tree — python/srmech/ + c/ — is MIT; the monorepo root stays GPL-3, by design: math is the scaffolding) and the RTD substrate-native-maths link fix. This graduation adds one docs-only change:
- Dropped the inline
**Status:** vX.Y.Z — …README banner. The PyPI project page already shows the published version at the top, and the changelog slice (appended by the fancy-pypi-readme hook) already carries "what's new", so the per-ship inline Status banner was redundant and required a manual bump every release. Version numbers inside the descriptive body prose are kept (they're historical and don't change).
No code change vs 0.8.1rc1; tools.total 310, ABI 3, numpy-free, MIT.
[0.8.1rc1] - 2026-06-17¶
MIT relicense + a broken RTD link fix (docs / metadata only — no code change). Two user-requested corrections ahead of the next production cut:
- Relicensed
GPL-3.0-or-later→MIT. The packagelicensefield in bothpyproject.toml/pyproject-pure.toml, the shippedLICENSEfile (now MIT text, © 2026 Steven Kirkland), the README License section, and every srmech-authored license header — all ~40c/source + header files, the 15cascade_catalog/*.toml+ 1worked_instances/*.tomlattestationlicensefields, and the_mcpb.pyClaude-Desktop-bundlelicense. The shipped tree (python/srmech/+c/) now contains zeroGPL-3.0-or-later. Left intentionally: the repo-rootLICENSE(monorepo / EMDR firmware — a separate project, stays GPL-3), datednotes/research scratch (historical),CHANGELOG.mdhistory, and the factual "cpuminer (GPLv2+)" attribution insrmech_sha256_batch.c(reworded to drop the now-moot GPL-3.0 forward-compat clause — no code was ever copied). - Fixed the substrate-native-maths RTD link in the README:
…readthedocs.io/substrate-native-maths/…→…readthedocs.io/en/latest/substrate-native-maths/…(the/en/latest/path segment was missing, so the link 404'd).
No public surface change: tools.total 310, ABI 3, numpy-free. 5-SSOT bumped 0.8.0 → 0.8.1rc1.
[0.8.0] - 2026-06-17¶
Production graduation to PyPI. The entire v0.7.5rc1 → rc173 development line graduates as 0.8.0 — 0.7.5 is skipped on production PyPI because the accumulated additions are a minor-version's worth of surface, so they ship as a minor bump rather than a patch. Identical package surface to 0.8.0rc2 (TestPyPI-verified, numpy-absent shipped wheel): no code change over the merged 0.7.5rc line beyond the version relabel and the PyPI-README / research-notebook refresh to cite 0.8.0 as the latest release. Headline additions consolidated into 0.8.0:
- numpy removed entirely (the rc69–rc134 carrier-removal arc) — no numpy dependency and no
[scientific]extra; every continuous-math op is a cascade of the 14 primitives over the numpy-freeMat/Vec/HVcarriers, fed zero-copy to the native dense kernels. A fresh numpy-absent venv imports and runs the whole package. - Native Class-M HDC core —
hdc.klein4_{bind,bundle,unbind,unbundle,similarity}+ streamingklein4_bundle_accumulate/klein4_bundle_resolvedispatch to C (~8–15× over pure-Python);klein4_unbundlenamesbundle's dual, so Class M is reversible up to capacity. - Corpus-scale, low-RAM graph partition — a sparse / iterative Class-L Fiedler (
laplacian.fiedler_sparse/normalized_cut_bisect) past the dense n≤256 eigensolver wall, plus out-of-corerecursive_cut+fiedler_sparse_file+ streamingtext.cooccurrence_topkto keep the encode bounded on edge devices. - Genome storage + file-management surface (
amsc.genome.*) — self-describing strand, in-place byte-splice edit,.chrbundles, loose↔packed, AMSC-compose; full standalone-C parity (caller-arena scratch, no compiled-in size caps). - Config-driven classes both directions —
dsl.generate_class_descriptoremits a[class].tomlfrom introspection (the inverse ofmake_class). - Exact-until-rotation DFT / eigenvalues (integer cyclotomic engine), the C-transpile of the transcendental cascade, the Rosetta-completeness ratchet (
c_exists_unbounddebt → 0), the PAL centralization, and removal of thesionaco-name mirror.
ABI 3; full native C / Python parity; describe()["tools"]["total"] = 310. Per-rc detail for the whole line is preserved in the [0.8.0rcN] and [0.7.5rcN] entries below.
[0.8.0rc2] - 2026-06-17¶
PyPI README numpy scrub — a real review this time. The rc1 README still advertised pip install srmech[scientific] and shipped a numpy-based Quick-start example, despite numpy having been removed in the carrier arc. The [scientific] extra is genuinely gone from both pyprojects (this was a README-only staleness, not a packaging bug), but the README never got a full sweep. rc2 fixes every stale reference:
- Install block — dropped the
pip install srmech[scientific]line (the extra does not exist);validation/collectorsextras unchanged. - Quick-start example — rewritten numpy-free:
laplacian.dense_laplacian(n, edges)(the real(n, edges) -> Matsignature, not a numpy adjacency) + plain-Python-list states. The exact block is extracted and run on the shipped wheel in a numpy-absent venv — it passes. - Prose —
so8.g2_subalgebra"rank-revealing numpy subset" → numpy-freeMat; Path A "closed-form algebra over numpy / scipy" → over the numpy-freeMat/Veccarriers;parallel_sector_dispatch"(native / IO / numpy) body" → "(native / IO)"; kuramoto "libm-trig tolerance" → native trig-cascade tolerance (the C build holds no libm); and thehurwitzbullet's nonexistenthurwitz_matrix(σ, θ)→ the actualcascade.the_one(σ, θ).to_matrix()+qm.hurwitz.hurwitz_planes()surface.
A full numpy | np. | scientific | scipy | libm | ndarray grep of the README now shows only correct context ("numpy-free", "no numpy", "no [scientific] extra", "holds no libm", or "scientific" in scare-quotes). No code change beyond the 5-SSOT bump; tools.total 310, ABI 3, numpy-free.
[0.8.0rc1] - 2026-06-17¶
Graduation candidate — the entire v0.7.5rc1 → rc173 line ships as 0.8.0. 0.7.5 is skipped on production PyPI: the accumulated additions are a minor-version's worth of surface, so they graduate as 0.8.0 rather than a 0.7.5 patch. This rc adds no new code beyond the already-merged 0.7.5rc line — it relabels the version and refreshes the PyPI README + research notebook to cite 0.8.0 as the latest release. Headline additions consolidated into 0.8.0:
- numpy removed entirely (the rc69–rc134 carrier-removal arc) — no numpy dependency and no
[scientific]extra; every continuous-math op is a cascade of the 14 primitives over the numpy-freeMat/Vec/HVcarriers, fed zero-copy to the native dense kernels. A fresh numpy-absent venv imports and runs the whole package. - Native Class-M HDC core —
hdc.klein4_{bind,bundle,unbind,unbundle,similarity}+ streamingklein4_bundle_accumulate/klein4_bundle_resolveall dispatch to C (~8–15× over pure-Python).klein4_unbundlenamesbundle's dual (bind-back +similaritycleanup), so Class M is reversible up to capacity — the per-class reversibility audit was corrected to match. - Corpus-scale, low-RAM graph partition — a sparse / iterative Class-L Fiedler (
laplacian.fiedler_sparse/normalized_cut_bisect) breaks the dense n≤256 eigensolver wall; out-of-corelaplacian.recursive_cut+fiedler_sparse_file+ streamingtext.cooccurrence_topkkeep the encode (not just the read) bounded for edge devices. - Genome storage + file-management surface (
amsc.genome.*) — self-describing strand, in-place byte-splice edit,.chrbundles, loose↔packed, AMSC-compose; full standalone-C parity (caller-arena scratch, no compiled-in size caps). - Config-driven classes both directions —
dsl.generate_class_descriptoremits a[class].tomlby introspecting what srmech already is (the inverse ofmake_class). - Exact-until-rotation DFT / eigenvalues (integer cyclotomic engine), the C-transpile of the transcendental cascade, the Rosetta-completeness ratchet (
c_exists_unbounddebt → 0), the PAL (platform-abstraction layer) centralization, and removal of thesionaco-name mirror.
ABI 3; full native C / Python parity; describe()["tools"]["total"] = 310. Per-rc detail for the whole line is preserved in the [0.7.5rcN] entries below.
[0.7.5rc173] - 2026-06-17¶
Remove the siona co-name mirror — free the name for a downstream srmech + inference package. From v0.4.4 the srmech wheel bundled a second top-level package, siona, that aliased every srmech.* object (pip install srmech → import siona returned the same objects), paired with a standalone siona metapackage on PyPI and a dedicated publish workflow. That mirror is now retired: srmech stays the scaffolding, and the siona name is reserved for a separate package (srmech + inference) developed in another session. No srmech public surface changes — tools.total stays 310, ABI stays 3, the package is numpy-free.
- Deleted the bundled alias package (
siona/__init__.py), its test (tests/test_siona_alias.py), the standalone metapackage (docs/srmech/siona/—pyproject.toml+README.md), and the.github/workflows/siona-publish.ymlpublish workflow. - De-mirrored the packaging in both
pyproject.tomlandpyproject-pure.toml: dropped thesiona = "srmech.cli:main"console script (thesrmechscript is unchanged),wheel.packages/ hatchpackages→["srmech"], and removed thesiona/**sdist include. - Prose scrub: the README "also bundles the
sionaalias" blurb, the CLI docstring's "(andsiona)", thetest_harmonics.pyco-name comment, and the Unreleased "siona statusCLI" deferred note are gone. The forward-lookingsrmech / siona processesbus-composition docstrings are kept — they anticipate the futuresiona(inference) package composing with srmech over the IPC bus. - Note: un-publishing the already-released
sionadistributions from PyPI / TestPyPI is a registry-side action that must be done by the maintainer; this rc only removes the mirror machinery from the repo so no future srmech release ships or publishessiona.
[0.7.5rc172] - 2026-06-17¶
klein4_unbundle — name the bundle's dual + correct the Class-M reversibility audit. The per-class reversibility audit (srmech_research_notebook.md §3.27) used to mark Class M (HDC) MIXED, pinning it on "bundle irreversible — no unbundle, only the similarity query." That under-stated it: bundle's dual is unbundle = unbind-on-the-bundle + similarity-cleanup, recoverable because the bundle keeps the relationship (the bound key→value pairs are still present in the superposition).
- New
srmech.amsc.hdc.klein4_unbundle(bundle, key)→ recover a bound value from a bundle by binding the key back:unbundle(S, kᵢ) = unbind(S, kᵢ) = bind(S, kᵢ)(self-inverse XOR). Exact for a single bound pair (unbundle(bind(k, v), k) == v); inside a multi-pair record it returns value-plus-crosstalk — denoise to the exact value withklein4_similarityagainst your value codebook (argmax_v similarity(unbundle(S, key), v)), recoverable up to the HDC bundle capacity. A purecomposition_of_cover the rc170-nativeklein4_bind; no new C symbol, no new param type (twoHVargs), ABI stays 3. - Reversibility audit corrected (
§3.27): Correction 2 + the Class-M table row + the "fundamentally one-way" list now readbundle ↔ unbundle(bind-back + cleanup), recoverable up to capacity — the capacity number is the recall bound (the analog of Class L's float-carrier floor), not proof of a missing inverse. Class M is reversible (capacity-bounded), not "MIXED/irreversible bundle."bundle↔unbundlejoinstlv_pack↔tlv_unpack(rc134) as an acted-on output of the audit. - Verified:
test_klein4_unbundle_rc172.py(single-pair exact recall;unbundle == bind == unbindcomposition identity; a 3-pair record whereunbundle+similarity-cleanup recovers each key's value). One new ToolEntry →tools.total309 → 310 (klein4_unbundle=composition_of_c). numpy-free; 5 SSOT bumped.
[0.7.5rc171] - 2026-06-17¶
§53 / F818 — wire the last klein4 op, closing the c_exists_unbound debt to ZERO. rc170 wired the klein4 core (bind / bundle / similarity); klein4_triality_cycle was the one remaining op with a shipped, ctypes-bound C twin (srmech_klein4_triality_cycle) that hdc.py never called. rc171 wires it — so every public srmech op that has a C twin now dispatches to it.
klein4_triality_cycle(v, *, inverse=False)now dispatches tosrmech_klein4_triality_cycle(in, n, inverse, out)when_native.has_native_klein4_triality_cycle()(new accessor), with the pure-Python table relabel as the bit-identical fallback. The C uses the same forward{0,2,3,1}/ inverse{0,3,1,2}3-cycle tables, so the native and pure paths agree exactly (forward, inverse, and the order-3 identityT∘T∘T = id). numpy-freearray('B')↔(c_uint8 * n)marshalling, same as rc170.- Measured (D=10000, shipped wheel, native vs forced-pure): ~8× (0.15 ms vs 1.19 ms), bit-for-bit identical.
- Rosetta (#928 down-only):
klein4_triality_cyclemovesc_exists_unbound → c_dispatched.CEIL_C_EXISTS_UNBOUND1 → 0 — the debt bucket is now empty. A newtest_c_exists_unbound_debt_is_closedasserts the bucket stays empty; a regression means a Python-only op shipped with an unbound C twin (wire it, don't raise the ceiling). No ToolEntry added —tools.totalstays 309. - Verified:
test_klein4_triality_native_rc171.py(pure correctness numpy-free + a native-gated differential class: forward / inverse / order-3 native == pure across a size sweep). ABI stays 3; 5 SSOT bumped.
[0.7.5rc170] - 2026-06-16¶
§53 / F818 — wire the Class-M klein4 core to its native C twins. The C srmech_klein4_bind / _bundle / _similarity have shipped in libsrmech and been ctypes-bound in _native.py for many rcs, but hdc.py never called them — klein4_bind / klein4_bundle / klein4_similarity ran pure-Python (the Rosetta c_exists_unbound debt: "a C twin exists, Python doesn't dispatch"). This is the gap behind "we are either missing C native code or don't have it wired into PyPI": not missing, just unwired. rc170 wires the dispatch, so the per-token HDC content-addressed walk (the F808 RBS-HDC recall) runs at C speed.
klein4_bind/klein4_bundle(chunk mode) /klein4_similaritynow dispatch tosrmech_klein4_*when_native.has_native_klein4_bind()(new accessor), with the pure-Python body as the complete, bit-identical alternative for no-C environments. The marshalling is numpy-freearray('B')↔(c_uint8 * n)(from_buffer_copyin;bytes(out)back);bundlebuilds a(POINTER(c_uint8) * n_vectors)pointer array. The chirality bundle (mode="chirality") stays pure-Python (it is not the native chunk path).- Coercion fast-path in
_as_klein4_buf: for an existing uint8 buffer (HV.buffer/array('B')/bytes— the common klein4 case) it bulk-copies at C speed + range-checks in onemax()pass instead of a per-element Python loop. This is what lets the native kernel win surface (otherwise the validation dominates) — and it also speeds the pure path ~3×. - Measured (D=10000, shipped manylinux wheel, native vs forced-pure):
klein4_bind~6×,klein4_similarity~2×,klein4_bundle(5 vectors) ~15× — all bit-for-bit identical to the pure-Python result. - Rosetta (#928 down-only):
klein4_bind/_bundle/_similaritymovec_exists_unbound → c_dispatched;klein4_unbind(==klein4_bind(c, a)) composes the now-dispatched bind →composition_of_c.CEIL_C_EXISTS_UNBOUND5 → 1 (onlyklein4_triality_cycle, whosesrmech_klein4_triality_cycletwin is not yet wired, remains the debt). No ToolEntry added —tools.totalstays 309. - Verified:
test_klein4_native_dispatch_rc170.py(pure-path correctness numpy-free + a native-gated differential class: bind / bundle / similarity native == pure across a size+count sweep, XOR self-inverse, chirality-bundle intact). ABI stays 3 (the klein4 C is not bound through the genome arena; additive). numpy-free, 5 SSOT bumped.
[0.7.5rc169] - 2026-06-16¶
§52 Part 2 COMPLETE — the out-of-core recursive partition driver (UPSTREAM §52 / F793). rc168 shipped the streaming-from-file Fiedler primitive (fiedler_sparse_file); rc169 builds the recursion driver on top of it, closing §52 Part 2 end-to-end: a corpus-scale co-occurrence graph can now be partitioned into community tomes with peak RAM bounded by the single largest sub-graph, not the whole structure.
- New
srmech.amsc.laplacian.recursive_cut(n, edges, weights=None, *, max_tome=256, work_dir=None, max_iters=250, max_depth=64)→{n_tomes, tome_paths, tomes, work_dir}. The same recursion as bisecting withnormalized_cut_bisectand recursing on each side, but out-of-core: the bounded graph (e.g. §52.1cooccurrence_topk's output) is written to a packed file (write_packed_graph); a disk-backed work queue of node-set files drives the recursion. Each step streams its sub-graph's induced edges (relabelled0..|S|-1) through the rc168fiedler_sparse_file(onlyO(|S|)resident), sign-splits, writes the two child node-sets to disk, and recurses until|S| ≤ max_tome(ormax_depth, or an uncuttable homogeneous block). The adjacency, every pending sub-graph, and every finished tome live on disk — peak RAM = the top-level sub-graph'sO(n)working vectors. A composition offiedler_sparse_file(C-dispatched) +write_packed_graph+ the disk-spilled recursion (composition_of_c). - Honest bound: the recursion descends into shrinking sub-graphs, so nothing larger than the top-level graph is ever resident; the
O(n)working-vector floor (the irreducible power-iteration state) remains, as in rc168. The driver is Python orchestration over the rc168 native streaming primitive — a standalone-C host already has the bounded primitive and can write its own recursion loop; a native-Crecursive_cutdriver is a tracked follow-on if needed. - Verified:
test_recursive_cut_rc169.py8/8 (two-clique → 2 tomes; four-clique chain → 4 tomes recursed; disk artifacts + tome-path round-trip; leaf cap; uncuttable-block termination; n<2; explicit-work_dir reuse; top-split ==normalized_cut_bisect). One new ToolEntry →tools.total308 → 309 (recursive_cut=composition_of_c); numpy-free, 5 SSOT bumped. §52 Part 2 (low-RAM ENCODE: streaming edge set + out-of-core partition) is now complete across both rungs.
[0.7.5rc168] - 2026-06-16¶
§52 Part 2 — the OUT-OF-CORE streaming Fiedler: bound the partition RAM, not just the edge set (UPSTREAM §52 / F793). rc167 (§52.1) streamed the docs and bounded the edge SET (cooccurrence_topk), closing the dominant ~2 GB encode peak. The remaining rung was the partition itself: fiedler_sparse (§51) is O(edges) and bounded per sub-graph, but it still held the whole edge list in RAM. rc168 ships the streaming-from-file primitive so even the bounded vocab × k graph need not be resident — the foundation for the full out-of-core recursive partition (rc169).
- New
srmech.amsc.laplacian.write_packed_graph(path, edges, weights=None)→ writes the bounded graph to a packed binary edge file (one 16-byte record per edge:uint32 u | uint32 v | double w, host byte order; records never straddle a read chunk). The edge list lives on disk, never fully resident — streams the rows out as it goes (peak RAM = one chunk). Returns the edge-record count. (non_computeIO;mcp_callable=False— a pure file writer.) - New
srmech.amsc.laplacian.fiedler_sparse_file(n, graph_path, *, max_iters=250)→ the streaming peer offiedler_sparsethat reads its adjacency from that file. Identical power iteration (so the result equalsfiedler_sparseon the same graph — bit-for-bit), but each matvec streams the file instead of holding the edges: only the O(n) working vectors are resident, so a low-RAM target can partition a graph whose edge list exceeds RAM. Composes §52.1cooccurrence_topkfor the bounded edge set → a fully low-RAM corpus-scale ENCODE for graph partition. - Native standalone-C
srmech_laplacian_fiedler_sparse_file(srmech_laplacian.c) — the same normalized-cut iteration assrmech_laplacian_fiedler_sparse, but the degree pass + each matvec STREAM the packed file via the PAL streaming-read (rc164); reuses the in-RAM helpers (fiedler_build_sp/fiedler_init/fiedler_deflate/fiedler_rescale/fiedler_update_sign) verbatim on the caller-arena vectors (≥8·n doubles, no compiled-in cap). JPL-clean (no goto/malloc/fabs; a per-record callback over chunked reads; a non-record-multiple read →BAD_INPUT). Bound in_native.py+has_native_fiedler_sparse_file();fiedler_sparse_filedispatches to it whenHAS_NATIVE(the bounded path), else reads the file in + runs the in-RAM cascade — the complete alternative (correct, not bounded; the bound is a native-path property). Additive symbol → ABI stays 3. - Honest bound: the streamed adjacency lives on disk, but the power iteration's length-n working vectors are irreducibly resident (random-access matvec) — so the bound is O(n) vectors + a bounded edge-stream buffer, the O(edges) adjacency off-RAM. For the most RAM-constrained targets even O(n) tiling is a further rung; this closes the O(edges) term, which dominates (
vocab × k≫n). - Verified (WSL pedantic
-Werror): newtest/test_srmech_fiedler_file.c12/12 — streamed cut == in-core cut bit-for-bit on a two-block graph, plus truncated-file / arena / n<2 / missing-file guards. Newtests/test_fiedler_sparse_file_rc168.py(round-trip + LE record format + streamed==in-RAM + clean cut + truncated/negative/short-weights guards + a 300-node two-clique partition past the n≤256 dense wall). Two new ToolEntries →tools.total306 → 308 (fiedler_sparse_file=c_dispatched,write_packed_graph=non_computein the Rosetta ledger; debt ceilings unchanged); numpy-free, 5 SSOT bumped.
[0.7.5rc167] - 2026-06-16¶
§52 streaming / bounded top-K co-occurrence — the LOW-RAM ENCODE peer of cooccurrence_edges (UPSTREAM §52 / F793). Building the co-occurrence graph from the wiki source peaks at 2.1–2.4 GB (the in-memory docs + the materialised ~9 M-edge list) while reading the pre-encoded tome-tree is only 48 MB — so an edge device ships the encoded genome and only reads it, but the encode itself was GB-scale. rc167 makes the encode low-RAM.
- New
srmech.amsc.text.cooccurrence_topk(docs, *, window=2, k=20, vocab=None, cap_slack=4, chunk_docs=2048)→{n, vocab, edges, weights, topk}. It streamsdocsone at a time (an iterable/generator — the corpus is never all resident) and keeps only a bounded top-K-per-node store via chunked merge: accumulate the full co-occurrence of eachchunk_docs-document chunk (bounded by the chunk), merge those weights into the running store, then truncate each node to ak·cap_slackcap. The peak isO(vocab × k·cap_slack + chunk)— never the full edge list. It is the explicit bounded analog of the §50 holographiccooccurrence_fold, and the bounded(n, edges, weights)triple is a drop-in forfiedler_sparse/normalized_cut_bisect(§51) — so the whole spectral-clump ENCODE (tokens → bounded graph → recursive cut) stays bounded. - Honest accuracy (verified): when a node's realized degree never exceeds the cap the result is bit-exact to the full-graph top-K (no truncation); the chunked merge keeps the full summed weight for every retained neighbour, so a heavy co-occurrence is never lost to a mid-accumulation eviction (proven on a distinct-weight chain at
k=1, cap_slack=1, and heavy core-core edges survive across 8-document chunks). Truncation only drops the long tail — which the downstream normalized-cut is robust to (top-K sparsification IS the production preprocessing; §51 stress test).vocab=Nonebuilds the vocabulary incrementally (single-pass streaming can't pre-rank by frequency). - New
tests/test_cooccurrence_topk_rc167.py(exactness + bound + distinct-weight + tiny-chunk heavy-hitter survival + Fiedler composition + arg validation). One new ToolEntry →tools.total305 → 306 (cooccurrence_topk=non_compute, the peer ofcooccurrence_edges); numpy-free, 5 SSOT bumped. Deferred next rung (F793 part 2): the out-of-core recursive partition (feeding §51 from a PAL-backed disk-spilled adjacency) — the partition is alreadyO(edges)and bounded per sub-graph, so part 1 closes the dominant encode peak.
[0.7.5rc166] - 2026-06-16¶
§51 sparse / iterative Class-L Fiedler — break the n≤256 dense-eigensolver wall for corpus-scale graph partitioning (issue #1097). The dense eigensolvers (fiedler_vector / symmetric_eigendecompose / jacobi_eigvals) cap at n ≤ 256, so a co-occurrence graph over >256 words can't be spectrally bisected — the blocker for the spectral-clumped loopshelf (partition a 244k-vocab graph into community tomes; F778 → F785/F786). rc166 ships the n-unbounded sparse peer.
- New
srmech.amsc.laplacian.fiedler_sparse(n, edges, weights, *, max_iters=250)→ a sign-bearingVec: power iteration on the normalized operatorB = I + D^(−1/2) W D^(−1/2)(= 2I − L_sym; eigenvalues in[0, 2]→ well-conditioned, unlikeσI − Lon a dense graph where the convergence ratio → 1 and it fails). It deflates the trivial√deg(λ₀) mode each step, leaving the Fiedler vector (λ₂ofL_sym); its sign is the normalized-cut bisection. Matvec-only → O(edges) time + memory,nunbounded. Stops early on sign-stability (5 stable-sign steps past a 20-iteration warmup). Noabs(): the max-magnitude rescale reads the Class-K magnitude-square then takes one Class-N root;√degisrational.sqrt. The init is a deterministic order-independent Class-I multiplicative scramble (not the parity vector[1,−1,…], which is orthogonal to the Fiedler on a block-ordered regular graph and would stall). - New
srmech.amsc.laplacian.normalized_cut_bisect(n, edges, weights)→(left, right)node-index lists by the sign offiedler_sparse— the ergonomic, O(edges), n-unbounded recursion primitive for spectral clumping (bisect, recurse on each side). - Correctness gate (the spec): the sparse Fiedler's sign partition is 100% identical to the dense
normalized_laplacian+symmetric_eigendecompose2nd-eigenvector reference on a worst-case dense two-community graph — including the parity-aligned layout that defeats a naive init. - Native standalone-C
srmech_laplacian_fiedler_sparse(matvec by edge, no CSR; caller-arena scratch → no compiled-in node cap, the bound is the caller's RAM; JPL-clean, no goto/malloc/fabs). Bound in_native.py+has_native_fiedler_sparse();fiedler_sparsedispatches to it whenHAS_NATIVE, else the pure-Python cascade — the complete alternative. Additive symbol → ABI stays 3. - Verified (WSL pedantic
-Werror): fiedler C-smoke 7/7 (clean two-block cut + arena / out-of-range / n<2 guards), hdc rc165 regression 8/8; ctypes sign-parity native==pure 100% across 12 random graphs (n up to 117, 1768 edges); JPL 6/6. Newtests/test_fiedler_sparse_rc166.py(gate + bisect + degenerate + native differential) +test/test_srmech_fiedler.c. Two new ToolEntries →tools.total303 → 305 (fiedler_sparse=c_dispatched,normalized_cut_bisect=composition_of_cin the Rosetta ledger; debt ceilings unchanged); 5 SSOT bumped.
[0.7.5rc165] - 2026-06-15¶
§50 native Klein-4 co-occurrence fold — the corpus-linear holographic-store build is now a single C call. rc155 shipped the streaming srmech_klein4_bundle_accumulate (one neighbour folded into a token's accumulator) and bound it, but the outer windowed fold — the loop that, for every token in a corpus, accumulates each ±window neighbour's code into that token's bundle — was still pure-Python in hdc.cooccurrence_fold. That loop is O(n_tokens · window), the actual bottleneck when the holographic store (§50.1 loopshelf / tome-leaves) is built at corpus scale. rc165 moves the whole fold into C and routes the Python op through it.
- New C symbol
srmech_klein4_cooccurrence_foldinsrmech_hdc.c(declared insrmech.h): given the flat per-token Klein-4 codes (n_codes × dim), a token-index stream (tok_idx, lengthn_tokens), a window anddim, it zero-inits then_codes × (1 + 2·dim)accumulator block and folds every in-window neighbour (j ≠ i,|i−j| ≤ window) of each token into that token's accumulator by reusingsrmech_klein4_bundle_accumulateper neighbour. JPL-clean (~32 lines, ≥2 asserts, no goto/malloc); a bad code byte (∉ {0,1,2,3}) or an out-of-range token index →SRMECH_ERR_BAD_INPUT. Standalone-complete — no caps, the caller owns theout_accsarena, native-authoritative when present. srmech.amsc.hdc.cooccurrence_foldgains a native fast-path (_native.has_native_klein4_fold()+n ≥ 2): it builds the flat code buffer +array('I')token-index stream +out_accsarena, makes the one C call, then resolves each token's accumulator withklein4_bundle_resolve. The pure-Python accumulation loop is unchanged and remains the complete alternative for numpy-free / no-C environments — proven bit-identical (every resolved per-token bundle matches,klein4_similarity == 1.0).- New
_nativebinding (srmech_klein4_cooccurrence_fold, ownhasattrguard) +has_native_klein4_fold()accessor (true iff both the fold andsrmech_klein4_bundle_accumulateare bound). Additive symbol → ABI stays 3. - New
test/test_srmech_hdc.c(8 cases): hand-computed accumulators for a 5-token / 3-code corpus (dim 4, window 1) check the C kernel directly —code1's accumulator= 2·code0 + 2·code2(n=4),resolve(code1) = {0,0,2,2}, plus bad-byte and out-of-range-index →BAD_INPUT. Newtests/test_klein4_cooccurrence_fold_rc165.py: Python differential parity (native fold == forced-pure via monkeypatch) across a(window, dim)sweep + the single-token edge case. - Verified (WSL pedantic
-Werror): hdc smoke 8/8, ctypes byte-parity over a 500-token / 20-code / dim-64 corpus (native fold == pure reimplementation), genome 70/70, ndjson 9/9, config 13/13. JPL 6/6 (srmech_klein4_cooccurrence_foldcarries ≥2 asserts — no new exempt). No new Python ToolEntry (cooccurrence_foldalready exists), sotools.totalunchanged at 303; 5 SSOT bumped.
[0.7.5rc164] - 2026-06-15¶
PAL streaming-read surface — the C library carries no raw stdio outside the PAL. The §B4 ndjson reader (srmech_ndjson_iter, the Class-C streaming line tokeniser) was the last module with its own fopen/fread/ferror/fclose — it pulls a file in 64 KiB chunks and assembles lines across chunk boundaries, so it needs a persistent read handle, not the rc161 whole-file read. rc164 adds that handle to the PAL and retrofits the reader onto it; grep fopen|fread|fwrite|fseek|fclose src/ is now zero outside srmech_platform.c.
- New PAL streaming-read surface in
srmech_platform.c/.h:srmech_plat_rstream_open/_read(read up tocapbytes,*out_n= count, 0 at EOF,SRMECH_ERR_IOonly on a real read error) /_close. Folded under the existing FILE backend — portable stdio, no OS split, no newhas_*accessor (it sharessrmech_plat_has_filesystem). The handle is a portableFILE*in opaque caller storage (_Static_assert-pinned), sosrmech_platform.hstays free of<stdio.h>; a bare-metal#elsestubs each toSRMECH_ERR_IO. srmech_ndjson.cdrops<stdio.h>forsrmech_platform.hand drives the rstream loop (open → read-chunk → process → close). The error-flag check the old reader did after a short read now lives insrmech_plat_rstream_read, so a mid-read failure still returnsSRMECH_ERR_IOrather than looking like clean EOF — byte-identical line semantics (chunk-straddling assembly, empty-line skip, trailing-CR strip, final-no-newline emission, 1-indexed lineno over all lines).- New
test/test_srmech_ndjson.c(the reader had no standalone C smoke — only Python-side coverage): 9 cases proving exactly the rstream-sensitive behaviours — a 70 000-byte line reassembled across the 64 KiB chunk boundary, the empty line skipped while lineno advances,\r\n→ CR-stripped, the final newline-less line emitted, and a missing file →SRMECH_ERR_IOwith no callbacks. - Verified (WSL pedantic
-Werror): ndjson smoke 9/9, genome 70/70, config 13/13, toml pass. JPL 6/6 (no new exempt — each rstream fn carries ≥2 asserts; the reader keeps itspath/cbasserts). C-only refactor;tools.totalunchanged 303; ABI stays 3 (additive symbols, hasattr-guarded); 5 SSOT bumped. With files (rc161/rc162), directories (rc163) and now streaming reads (rc164) all centralized, every OS touch in the C library lives in the PAL TU.
[0.7.5rc163] - 2026-06-15¶
PAL directory-iteration surface — the genome's last #ifdef is gone. rc162 retrofitted the genome's whole-file I/O onto the rc161 PAL FILE surface but left one OS-specific touch: genome_list_chr (the §43 loose↔packed *.chr scan) still carried its own #if defined(_WIN32) FindFirstFile / #else opendir-readdir block. Per the same user direction ("fix genome to use PAL correctly") this becomes the fourth PAL surface (threads rc4 / streams rc5 / files rc161 / directories rc163) — the genome now contains zero #ifdef.
- New PAL dir surface in
srmech_platform.c/.h:srmech_plat_has_dirlist()+ asrmech_plat_dir_thandle (opaque OS-handle storage + apendingname slot for Win32's FindFirstFile first-entry lookahead) +srmech_plat_dir_open/_dir_next/_dir_close. POSIX usesopendir/readdir/closedir; Windows usesFindFirstFileA/FindNextFileA/FindClose(the open call reads the first entry, buffered intopending, drained on the first_dir_next). The iterator yields every entry name (incl../..); the caller filters by suffix. A bare-metal target reportshas_dirlist() == 0and every call returnsSRMECH_ERR_IO._Static_assertpinsDIR*/HANDLEinto the handle storage; each_dir_nextcopies the name with an OVERFLOW guard (JPL Rule 2 bounded). srmech_genome.c:genome_list_chrrewritten to drive the PAL iterator (open → loop_dir_nextuntilhave==0→genome_chr_name_oksuffix filter →close); the#if defined(_WIN32) #include <windows.h> #else #include <dirent.h> #endifblock is deleted. The genome is now fully#ifdef-free — every OS touch (file + dir) lives in the PAL TU.- Behaviour is byte-identical — same POSIX/Win32 directory primitives, just centralized.
genome_explode/genome_pack(the onlygenome_list_chrcallers) are unaffected; the canonical-sort pack order is preserved. - Verified: genome C-smoke 70/70 under pedantic
-Werror(explode/pack exercisegenome_list_chr— the byte-for-byte pack round-trip proves the PAL scan is equivalent), config C-smoke 13/13 + TOML regression; JPL 6/6 (srmech_plat_has_dirlistadded to the Rule-5 trivial-accessor exempt list besidehas_threads/has_streams/has_filesystem). C-only refactor (no Python/dispatch change);tools.totalunchanged at 303; ABI stays 3; 5 SSOT bumped. - Remaining genome/ndjson OS touch needing a new PAL surface (rc164 candidate): the ndjson streaming reader (a chunked tokenizer — needs an open→read-chunk→close streaming-file PAL primitive, not whole-file read).
[0.7.5rc162] - 2026-06-15¶
Genome file I/O retrofitted onto the PAL — the genome carries no raw OS file calls. rc161 added the PAL FILE surface (srmech_plat_file_read/_read_region/_write/_size in srmech_platform.c — the single OS-file TU). srmech_genome.c still had its OWN private stdio helpers (genome_write_file/genome_read_file/genome_read_region/genome_file_size) + a raw fopen existence probe — predating the PAL FILE surface. Per user direction ("we also must fix genome to use PAL correctly. that's why we established it long ago") they now delegate to the PAL.
srmech_genome.c: the 4 file helpers become thin PAL delegations —genome_write_file→srmech_plat_file_write(mode string →appendflag),genome_read_file→srmech_plat_file_read,genome_read_region→ keeps itslen > capOVERFLOW guard thensrmech_plat_file_read_region,genome_file_size→srmech_plat_file_size;genome_body_existsdrops its rawfopenprobe forsrmech_plat_file_size. Zerofopen/fread/fwrite/fseek/ftell/fcloseremain in the genome — the OS file surface lives in exactly one TU (the PAL), like threads (rc4) / streams (rc5).srmech_platform.c:srmech_plat_file_readgains aferrorcheck (a mid-read error returnsSRMECH_ERR_IOrather than looking like clean EOF) so the genome's delegation loses none of the robustness its own reader had.- Behaviour is byte-identical — the PAL primitives are the same portable stdio the genome used, just centralized. This is a pure centralization refactor, not a behaviour change; the byte-parity-critical genome write path (
manifest.json+turns.bin) is untouched in output. - Verified: genome C-smoke 70/70 under pedantic
-Werror— every byte-equality assertion (save/load/window/append + the §45 remove-byte-splice/replace-in-place + the §43 export/import-seed/import-append-byte-for-byte/explode/pack-byte-for-byte + the 300-chromosome arena case) passes through the PAL-delegated I/O, proving byte-parity. Config C-smoke 13/13 + TOML regression; JPL 6/6 (srmech_genome.cstill adds no Rule-5 exemptions — the delegating helpers keep their ≥2 asserts). C-only refactor (no Python/dispatch change);tools.totalunchanged at 303; ABI stays 3; 5 SSOT bumped. - Remaining genome/ndjson OS touches needing new PAL surfaces (rc163 candidates): the ndjson streaming reader (a chunked tokenizer — needs an open→read-chunk→close streaming-file PAL primitive, not whole-file read) and
genome_list_chr's directory enumeration (opendir/FindFirstFile— needs a dir-iteration PAL primitive; the genome's last#ifdef _WIN32).
[0.7.5rc161] - 2026-06-15¶
Config-driven library limits (the seed of a config-driven dispatch layer) + PAL file surface + orphan removal. After the rc156–rc160 sweep made every compiled-in problem-SIZE cap a caller-arena bound, one residual #define remained that was a deliberate sanity ceiling, not a buffer size: the Hermitian-eig node guard (SRMECH_HERMITIAN_WS_MAX_NODES = 2048). Per user direction ("sanity ceiling should be config driven, not hard coded" / "we were wanting to embrace event driven"), it becomes a runtime config value the C library reads from a TOML descriptor — the first such value, with the registry extensible to more.
- New C
srmech_config.c:srmech_config_hermitian_max_nodes()(the live ceiling; built-in default 2048, preserved exactly until overridden),srmech_config_load_toml(toml, len, ws, ws_len)(parse a caller-held blob — MCU-safe, no filesystem needed),srmech_config_load_file(path, ws, ws_len)(read the file through the PAL, then parse),srmech_config_reset_defaults(). Both loaders parse with the rc159srmech_tomlparser into the caller arena (no malloc); a missing/partial config keeps the current value. The Hermitian_wskernel's guard now reads the getter, so a raised config lifts the bound in lockstep —[hermitian] max_nodes = 5000is honoured (the old 2048 was never a hard cap, just the default). - PAL file surface (rc161) —
srmech_platform.c/.hgainssrmech_plat_has_filesystem()+file_read/file_read_region/file_write/file_size. File I/O is portable stdio, so POSIX and Windows share ONE implementation; a bare-metal target reportshas_filesystem() == 0and every call returnsSRMECH_ERR_IO(the caller feeds bytes via_load_tomlinstead). This is the third PAL surface (threads rc4 / streams rc5 / files rc161) — the single TU where OS file code lives;srmech_configis its first consumer. (The genome's own raw stdio retrofit onto this surface is the rc162 follow-up.) - Orphan removed: the no-
_wssrmech_hermitian_eigendecompose(a 1 MiB thread-local static with its own n≤256 cap and no live caller since the_wsentry shipped) is deleted fromsrmech_laplacian.c/srmech.h/_native.py. The reentrant_wsentry is the only native Hermitian path now;MAX_NATIVE_HERMITIAN_NODES(Python) is documented as the default mirror, no longer the dispatch gate —laplacian.pygates on_native.config_hermitian_max_nodes(). - Python config wrappers in
_native.py:has_native_config()+config_hermitian_max_nodes()(native authority when present, else the built-in default — a no-native host has no native ceiling, the pure-Python Jacobi being uncapped) +config_load_toml/config_load_file/config_reset_defaults(no-ops with no native lib). All four C symbols are hasattr-guarded in the ctypes shim → additive, ABI stays 3. - Verified: new
test_srmech_config.cC-smoke 13/13 (default 2048 → override down 512 → override up 5000 past the old cap → partial-config preserved → reset → PAL file write/read round-trip → missing-fileSRMECH_ERR_IO); whole-tree pedantic-Werror; genome C-smoke 70/70 (laplacian TU shares the lib, incl. the 300-chromosome arena-bound case) + TOML C-smoke regression; JPL 6/6 (srmech_plat_has_filesystemadded to the Rule-5 trivial-accessor exempt list besidehas_threads/has_streams). Newtest_config_layer_rc161.py(numpy-free).tools.totalunchanged at 303 (config is library policy, not a compute ToolEntry); 5 SSOT bumped.
[0.7.5rc160] - 2026-06-15¶
C standalone-complete honor sweep, part 5 — the JSON-object writer child cap (the FINAL site; sweep complete). srmech_json.c's canonical writer (srmech_json_write, byte-identical to json.dumps(sort_keys=True, ensure_ascii=False) — the keystone the genome manifest.json mirror rides) sorted each object's keys through a per-emit-frame uint32_t order[SRMECH_JSON_MAX_CHILDREN = 256], and the builder srmech_json_new_object rejected n > 256 — capping any object at 256 keys. The emit-frame stack also lived in a MAX_DEPTH × 256-wide thread-local static. JSON arrays already grew arena-backed (rc154); only OBJECTS were capped. This was the last compiled-in problem-size cap in the C library.
- C: the emit frame's inline
order[256]becomes auint32_t *ordercarved from a caller arena. The cappedsrmech_json_writeis replaced bysrmech_json_write_ws(v, buf, buf_len, out_len, ws, ws_len)+srmech_json_write_arena_bytes(v)— a pre-pass (json_emit_dims, an explicit MAX_DEPTH-bounded walk) sizes the arena to the actual tree (deepest nesting × widest object), the writer carves the frame stack + the shared key-order pool fromws, and the emit/sort algorithm is unchanged → the emitted bytes are byte-identical. Thesrmech_json_new_objectn > 256reject is dropped (keys/values are arena-copied).SRMECH_JSON_MAX_CHILDRENis removed fromsrmech.h;SRMECH_JSON_MAX_DEPTH(recursion guard) stays. So an object is bounded only by the caller's RAM — standalone-complete on a host or an MCU alike. - Callers repointed: genome's two C writers (
genome_build_manifestSAVE +genome_chr_build_file.chr export) hand the writer the builder's own untouched arena tail as its key-sort scratch — no extra allocation, no genome-arena formula change (the existing 64 KiB manifest slop absorbs it). The Python binding (genome_catalog_cin_native.py) sizes awsviasrmech_json_write_arena_bytesand calls_ws. ABI stays 3 — the old capped symbol is removed, the new_wsname is hasattr-guarded in the ctypes shim (the additive_ws-variant pattern of rc158's dense-solve /srmech_hermitian_eigendecompose_ws). - Verified:
test_srmech_json.cgains a 400-key-object case (> the old 256 cap) provingnew_objectaccepts it AND the writer emits it canonically (sortedk000..k399). Full JSON C-smoke 24/24 + genome C-smoke 70/70 (incl. the manifest write via_wsand a>256-chromosome genome) pass under pedantic-Werror; JPL 6/6.tools.totalunchanged at 303 (a C-internal arena refactor — no ToolEntry surface moves); 5 SSOT bumped. - The C standalone-complete honor is now universally applied across the whole srmech C library — every compiled-in problem-size cap (Laplacian / dense-solve / exact-DFT / HDC-bundle / TOML / JSON) is now a caller-arena bound (caller's RAM), per
[[feedback_c_must_be_standalone_complete_no_python_fallback]]. (Also corrects the stale "don't vendor a TOML/JSON parser in C" Phase-B5 note — the parsers exist and are wanted; a C-only host needs them to read JSON manifests / TOML descriptors with no Python.)
[0.7.5rc159] - 2026-06-15¶
C standalone-complete honor sweep, part 4 — the TOML parser child cap. srmech_toml.c (the malloc-free, caller-arena C TOML parser a C-only / MCU host uses to read descriptors with no Python) capped a single table or array at SRMECH_TOML_MAX_CHILDREN = 256 entries — the array path staged collected element pointers in a fixed local items[256] and every collect loop was bounded by the cap, returning SRMECH_ERR_OVERFLOW past 256.
- Array parse (
toml_parse_array): theitems[256]stack staging array is replaced by an arena linked list (a newtoml_alist_node_t, mirroring how the builder tables already collect their entries), copied right-sized into the arena on close. No fixed staging → the element count is bounded only by the caller arena. - Table / inline-table / array-of-tables (
toml_btable_append/toml_parse_inline_table/toml_atable_append): thecount >= MAX_CHILDRENrejects are dropped (entries are already arena-allocated linked-list nodes), and the collect loops are rebounded from the compiledMAX_CHILDRENto the remaining input lengthp->n— a genuine runtime upper bound (an element consumes ≥ 1 input byte, so it cannot exceedp->n), still JPL-Rule-2-bounded but not a compiled-in problem-size cap. SRMECH_TOML_MAX_CHILDRENis removed fromsrmech.h;SRMECH_TOML_MAX_DEPTH(a recursion-depth guard, not a problem-size cap) stays. No signature change, no Python-binding churn — the TOML parser owns its ownp->arena.- Verified:
test_srmech_toml.cgains a 400-element array + 400-key root table case (both > the old 256 cap) — all entries survive. The full existing TOML C-smoke (scalars / both string flavours / nested + multiline arrays / inline tables / dotted keys / table reopening / arrays-of-tables / unicode-escape — the byte-shape parse) still passes. C pedantic-Werror; JPL 6/6. ABI 3;tools.totalunchanged at 303 (TOML parsing is Python-unreachable; descriptors usetomli— so no Python surface moves); 5 SSOT bumped. - Remaining honor backlog: the JSON-object writer child cap (
srmech_json.cper-emit-frameorder[256]) — rc160, the final site (a parity-criticalsrmech_json_writearena variant).
[0.7.5rc158] - 2026-06-15¶
C standalone-complete honor sweep, part 3 — the dense-solve caller arena (the one genuine-scratch site). srmech_dense_solve.c carried SRMECH_DENSE_SOLVE_MAX_N / _MAX_RHS = 256 plus a 1 MiB thread-local static augmented [A|B] buffer, and the Python mat_solve bounds-gated on n,w ≤ 256 then rescued an over-cap / singular native return into the pure-Python exact-rational solve — the rc153 anti-pattern. Unlike the rc156/rc157 sites, the augmented matrix is genuine scratch (Gauss–Jordan mutates a copy of the const A and B), so it cannot be eliminated — it must come from the caller's arena (the v0.7.5rc154 genome precedent).
- C: the capped
srmech_dense_solve_f64is renamed tosrmech_dense_solve_f64_ws(n, nrhs, A, B, out_X, ws, ws_len)— the augmented[A|B]matrix is bump-carved (8-byte-aligned) from the caller arenaws, so there is no compiled-in size cap; the only bound is the caller's RAM (a host sizes it large, a microcontroller small). Newsrmech_dense_solve_arena_bytes(n, nrhs)defines the exact byte count the caller sizeswsfrom. The static buffer + the three#definecaps are gone. An under-sized arena returnsSRMECH_ERR_OVERFLOW; a singularAstill returnsSRMECH_ERR_BAD_INPUT. JPL-clean (no malloc — bump over a caller buffer), pedantic-Werror. - Python:
mat_solvedrops the≤256bounds-gate and the OVERFLOW/BAD_INPUT→exact rescue. When native is present it sizeswsper call viasrmech_dense_solve_arena_bytes, calls the kernel as the authority, and translates a singularSRMECH_ERR_BAD_INPUTto the documentedZeroDivisionError. The pure-Python exact-rational Gauss–Jordan (_solve_exact, Class-N, itself uncapped) is the complete alternative implementation for no-C hosts (pure wheel / Pyodide) — not a fallback rescue.mat_lstsq/_mat_solve_complex/ lmmse / map_ml ridemat_solve, so they inherit the no-cap solve automatically. - ABI stays 3: the old capped symbol is removed, the new
_wsname is hasattr-guarded in the ctypes shim (a stale lib lacking it cleanly degrades to pure-Python) — the additive_ws-variant pattern ofsrmech_hermitian_eigendecompose_ws, not a wire-format change to a bound symbol. - Also fixes a stale rc156 header doc: the
srmech_exact_dft_i64comment still saidN ∈ [2, 4096]after rc156 lifted that cap — corrected to "any power of two ≥ 2; the genuine bound is int64 element magnitude." - Proven at n = 300 > 256 (
test_c_standalone_honor_dense_solve_rc158.py, numpy-free): nativemat_solveaccepts a 300×300 system (A·Xreconstructs a knownX) the old cap forbade; native == forced-pure on a diagonal n=300 system and a dense n=12 system; singularAraisesZeroDivisionErroron both paths.tools.totalunchanged at 303; 5 SSOT bumped. - Remaining honor backlog: the TOML / JSON-object parser child caps (
items[256]/order[256]) — lowest priority (small descriptor corpus, manifest objects ≤ 9 keys).
[0.7.5rc157] - 2026-06-15¶
C standalone-complete honor sweep, part 2 — lift the four Class-L node caps (graph Laplacian / normalized / Jacobi eigvals / dense matmul). srmech_laplacian.c carried SRMECH_LAPLACIAN_MAX_NODES 256 on four ops; the audit found none of them actually needs scratch, so the cap was a gratuitous rejection of larger valid graphs/matrices a C-only / MCU host could serve.
srmech_graph_dense_laplacian— thedegree[256]stack array is eliminated:L = D−Ais row-local (deg_rdepends only on rowr, read before rowris overwritten), so it is computed per-row into the caller's matrix. No scratch.srmech_graph_normalized_laplacian— thed_inv_sqrt[256]stack array is eliminated:d^(−1/2)is stashed in the diagonal (whichL_symoverwrites anyway), used for the off-diagonal pass, then the diagonal is finalised to {0,1}. No scratch.srmech_jacobi_eigvals— already rotates the caller'smatrixin place (eigenvalues read off the diagonal); then > 256cap was gratuitous and is removed.srmech_dense_matmul_complex— accumulates in scalar locals and writes the caller's output buffer; them/k/n > 256cap is removed.- Python:
_can_dispatch_native(n)drops then ≤ MAX_NATIVE_NODESbounds-gate (the rc153 anti-pattern) andmat_matmuldrops its inline≤256check, so native is authoritative for any size and the pure-Python path is the complete alternative only when there is no C. (mat_solve/ the Hermitian legacy_wsstatic buffer are unchanged — dense-solve is the next rc; the Hermitian legacy path keepsSRMECH_LAPLACIAN_MAX_NODES, which now sizes only that workspace.) - Proven at n = 300 > 256 (
test_c_standalone_honor_laplacian_rc157.py, numpy-free):dense_laplacianandnormalized_laplaciannative == forced-pure across the full 300×300 matrix;jacobi_eigvalsaccepts n=300 (diagonal-spectrum correctness) + native==pure at n=72;mat_matmulaccepts n=300 (A·I == A) + native==pure at n=96. All 131 existing Class-L parity tests still green. C pedantic-Werror; JPL 6/6; ABI 3;tools.totalunchanged at 303; 5 SSOT bumped. - Remaining honor backlog: dense-solve (
aug[]static → caller arena + drop themat_solveOVERFLOW-rescue) and the TOML / JSON-object parser child caps.
[0.7.5rc156] - 2026-06-15¶
C standalone-complete honor sweep, part 1 — lift the two no-scratch size caps (exact-DFT + HDC batch bundle). A full audit (post-rc155) of the whole C library against the rc154 standalone-complete honor ([[feedback_c_must_be_standalone_complete_no_python_fallback]]) found six native surfaces still carrying pre-rc154 compiled-in problem-SIZE caps. This rc fixes the two cheapest — caps that bound caller-INPUT size with no scratch to arena-size (the kernels already write only into caller-supplied memory), so a C-only / microcontroller host with the RAM for the larger problem was refused for nothing.
SRMECH_EXACT_DFT_MAX_N 4096removed (srmech_exact_dft.c): the exact integer DFT writes straight into the caller'sout_re/out_im(lengthN·N/2), so the bound is the caller's own buffers, never a compiled limit. The int64 element magnitude is a genuine fixed-width-integer domain (the Python wrapper keepsN·max|signal|int64-safe and routes larger magnitudes to its bignum path) — that is NOT a size cap and stays. A standalone-C host can now take any power-of-twoN.SRMECH_HDC_MAX_BUNDLE_N 257removed (srmech_hdc.csrmech_hdc_bundle/srmech_polar_bundle/srmech_klein4_bundle): the batch bundles majority-vote over a caller-resident pointer array into scalaruint32/int32counters — no scratch, no real bound until2³²vectors. The Pythonhdc.bundle/bundle_with_tiespre-bound (theMAX_BUNDLE_Nconstant) is removed — it was also strangling the otherwise-uncapped pure-Python loop. The odd-count BSC requirement (a genuine clean-majority constraint) stays. API change (we are the only consumer): thesrmech.amsc.hdc.MAX_BUNDLE_Nconstant is gone, not deprecated.- Proven at sizes the old caps forbade (
test_c_standalone_honor_rc156.py, numpy-free): native exact-DFT acceptsN=8192(was OVERFLOW) and matches the pure negacyclic loop bin-for-bin;hdc.bundleof 301 vectors native == forced-pure;bundle_with_tiesof 300 runs (noValueError); the three native batch-bundle symbols return OK forn_vectors=301. C compiled pedantic-Werror; JPL 6/6; ABI stays 3 (no symbol changes);describe()["tools"]["total"]unchanged at 303 (MAX_BUNDLE_Nwas a constant, not a tool). 5 SSOT bumped. - Remaining honor backlog (later rcs in this sweep): the Laplacian / dense-solve arena-carves (
LAPLACIAN_MAX_NODES/DENSE_SOLVE_MAX_N256 + their Python bounds-gate / OVERFLOW-rescue) and the TOML / JSON-object parser child caps.
[0.7.5rc155] - 2026-06-15¶
Streaming holographic Klein-4 bundle-accumulate — klein4_bundle_accumulate / klein4_bundle_resolve / cooccurrence_fold (UPSTREAM §50, F758). The batch klein4_bundle needs every vector resident at once; this rc adds the incremental form, so a holographic co-occurrence store never materialises its inputs and stays fixed-width (it grows with the #coordinates D, not the #folded vectors — the fix for "why is the HDC object growing to gigs?"). It is the missing holographic DUAL of the explicit-edge §17-U1 cooccurrence_edges.
srmech.amsc.hdc.klein4_bundle_accumulate(acc, v)folds one Klein-4 vector into a fixed-width(1 + 2*D)uint32 accumulator (acc[0]=n, then per-coordinate bit-0 / bit-1 1-counts);acc=Noneauto-creates one sized tov; returnsacc(mutated in place).klein4_bundle_resolve(acc)resolves it by strict per-bit majority (tie → 0) to theHVcarrier — bit-identical toklein4_bundleover the same vectors, so a resolved bundle drops straight into a genome tome-leaf (§50.1) or aklein4_similaritycleanup. The accumulator is the caller's memory — its width is the architecture (1 + 2*Duint32), no compiled-in cap.cooccurrence_fold(tokens, *, window, dim, seed)— the holographic co-occurrence store: folds every(token, neighbour)within±windowinto a per-token fixed-width bundle WITHOUT building the edge list, so the store grows with VOCAB (Heaps' law) not edges. Returns{"bundles", "codes", "vocab", "n_tokens"}; read a relationship out withklein4_similarity(bundles[a], codes[b]). LOSSY (superposition crosstalk, F584) — the bounded associative TAIL to §17-U1's small exact working set (the F119/F529 two-tier at the primitive level).- Standalone-C kernels
srmech_klein4_bundle_accumulate/_resolve(no Python callback, no libpython dep — the rc154 standalone-C discipline) run the fold at corpus scale; the Python ops dispatch to them when present and are byte-identical to the pure path. Pure-Python is the complete alternative for a no-C environment. Additive symbols → ABI stays 3. - Verified:
test_holo_bundle_accumulate_rc155.pyproves the incremental fold == the batch bundle bit-for-bit across n∈{1,2,3,4,5,8,16,33,64} (the even-n tie→0 boundary included), the accumulator stays fixed-width, native==forced-pure, and the co-occurrence readout scores neighbours above non-neighbours. C compiled pedantic-Werror; JPL 6/6. - Three new public callables (
describe()["tools"]["total"]300 → 303): 2 Rosettac_dispatched+ 1composition_of_c(no debt-ceiling change); 3 ToolEntries + 3 ledger rows + 7 count-tests bumped. numpy-free; 5 SSOT bumped.
[0.7.5rc154] - 2026-06-15¶
Genome C is standalone-complete — caller-arena scratch, no compiled-in caps, no Python fallback. rc153's dispatch was bounds-gated: a genome over the C's 16-MiB static scratch arena, or over 256 chromosomes, fell back to pure-Python — so the C was NOT a 1:1 mirror of Python, and a C-only host (no Python) silently could not handle a large genome. rc154 makes the genome C handle its full input domain standalone: all scratch is carved from a caller-supplied arena (capacity is the caller's RAM — host-large or MCU-small — not a baked-in ceiling), and the Python dispatch is native-authoritative when present with no try-native-except-retry-pure.
- Caller-arena bump allocator (
c/src/srmech_genome.c): agenome_arena_t {base, cap, off}+genome_arena_init/alloc/tailcarves ALL genome scratch (body staging, the manifest string arrays, the.chrregion buffer, the JSON staging) from the callerwsarena. The compiled-in body buffer and theSRMECH_GENOME_MAX_CHROMS(256) cap are deleted. Capacity is now defined by the layout: the newsrmech_genome_arena_bytes(body_len, n_chroms, region_len)returns the exact arena size the operation needs — it scales withn_chroms, so it is architecture-derived, not a magic multiplier. Adding this symbol does not bump ABI (stays 3). - The real 256-cap was in
srmech_json(SRMECH_JSON_MAX_CHILDREN = 256), not the genome arrays.c/src/srmech_json.cis reworked so a JSON array is unbounded (arena-backed growable parser frame; thenew_array256-guard removed) andjson_parse_stringpre-scans the string span (O(input²) per-string staging → O(input)). Objects stay capped (the canonical writer's key-sort uses a fixedorder[256]; genome objects have ≤9 keys). All byte-parity-preserving —json.dumps-identical output is unchanged. srmech.amsc._native: arena sizing is now derived from the C formula —_genome_arena(body_len, n_chroms, region_len)callssrmech_genome_arena_bytes(so Python and C agree on capacity by construction) and grows the reused workspace as needed; the fixed 16-MiB arena + theGENOME_NATIVE_*bound constants are gone.srmech.amsc.genome: the dispatch is de-gated — native is authoritative when present. The cheap, caller-facingValueErrorcases (a duplicate chromosome label ingenome_import/genome_pack) are validated in Python BEFORE the native call, so a native non-OK status is unambiguously an integrity failure and is translated toGenomeBoundingError(no silent fall-through to a pure recompute). Pure-Python remains the complete alternative for a no-C environment — never a rescue path behind the native call.- Verified past BOTH old caps: a new differential (
tests/test_genome_native_dispatch_rc153.py::test_save_byte_identical_over_old_caps) builds a genome with 300 chromosomes (>256) AND a >16-MiB body and asserts native and forced-pure writeturns.bin+manifest.jsonbyte-identical; the C smoke gains a 300-chromosome case. WSL2 ctypes byte-parity re-confirmed (genome 70/70, json 20/20, pedantic-Werror); JPL 6/6. - No new public callable —
describe()["tools"]["total"]stays 300; ABI stays 3 (the genome C is not bound into the carrier-op ABI). 5 SSOT bumped. numpy-free.
[0.7.5rc153] - 2026-06-15¶
§49 binding mile — the 11 srmech_genome_* C symbols are now bound + dispatched. The C library already shipped the whole genome file-management family (the §41/§43/§44/§45 mirror), but srmech.amsc.genome never called it — it stayed pure-Python (the §38 / F708 "C exists, Python doesn't call it" gap). rc153 binds all 11 symbols in srmech.amsc._native and routes genome.py through them when HAS_NATIVE, so a C-host genome path is real AND the Python path is native-accelerated.
srmech.amsc._nativebindssrmech_genome_{save,load,catalog,append,remove,replace,window,export,import,explode,pack}+srmech_json_write(argtypes/restype,hasattr-guarded), and adds 11genome_*_cwrappers +has_native_genome()+ aNativeGenomeError+ a lazily-allocated 16-MiB workspace arena.genome_catalog_cserialises the nativesrmech_json_value_t*manifest tree viasrmech_json_write(byte-identical tojson.dumps) and returns the JSON text.srmech.amsc.genomeroutes all 11 public ops: the native path is a pure accelerator (it writesturns.bin+manifest.json+ the.chrbundles byte-for-byte identically — proven across rc143–rc152's byte-parity harness), and on ANY native error it falls back to pure-Python (so an oversized genome past the 16-MiB static scratch, and every preciseGenomeBoundingError/ValueError, are preserved).genome_packpacks natively into a scratch dir and only adoptsdeston full success (pack is multi-step → not atomic → the realdestmust stay pristine for a clean fallback).- Differential test (
tests/test_genome_native_dispatch_rc153.py, 11 cases): every op runs once native + once pure-Python (forced) and asserts the on-disk strand + manifest are byte-identical and the returns match. SKIPS when the native genome surface is not built (pure-wheel / numpy-absent dev tree); the CI test-matrix cells build it. - No new public callable —
describe()["tools"]["total"]stays 300; ABI stays 3 (binding existing symbols / the genome C is not wired into the carrier-op ABI). 5 SSOT bumped. numpy-free.
[0.7.5rc152] - 2026-06-14¶
§43 bundling↔AMSC compose — genome_register_attested (F729). A genome's exploded .chr dir can now be registered with the AMSC catalog: each chromosome becomes its own attested source, discoverable through list_attested_sources. This composes the EXISTING AMSC framework — it does not mint a parallel attestation (F730).
- New
genome_register_attested(chr_dir, amsc_root, *, source)— for every<label>.chrinchr_dir(agenome_explodeoutput), writes a per-chromosome<amsc_root>/<label>/descriptor.toml(theliterature_curatedadapter — no live fetch, NDJSON committed directly) +row.ndjson, then callssrmech.amsc.catalog.register_attested_root. After it,srmech.amsc.catalog.list_attested_sources()surfaces one source per chromosome (keyed by its label,data_schema_id = "srmech.genome_chromosome.row.v1"). Returns{ok, amsc_root, source, chromosomes:[{label, source_key, descriptor_path, row_path, region_sha256}], register}. - No parallel attestation (F730). The chromosome's OWN MPR attestation — carried in its
.chr(attestation.response_sha256== the region hash) and echoed into itsrow.ndjsonregion_sha256— IS the provenance. The descriptor registers it; it does not re-hash or re-attest. The generateddescriptor.tomlvalidates under the AMSCload_descriptorcontract (all six required sections + fields). - Regression-pinned (
tests/test_genome_amsc_register_rc152.py, 5 cases): each chromosome surfaces inlist_attested_sources(keyed by label,literature_curated); the generated descriptors validate underload_descriptor; the row'sregion_sha256IS the.chr's existingattestation.response_sha256(composed, not re-minted); empty-dir guard; idempotent re-registration. numpy-free. - One new public callable (
genome_register_attested,non_computeRosetta bucket):describe()["tools"]["total"]299 → 300 (1 ToolEntry + 1 Rosetta-ledger row + 7 count-tests bumped). Python-only (the AMSC catalog/descriptor layer is Python by scope); ABI stays 3. 5 SSOT bumped.
[0.7.5rc151] - 2026-06-14¶
§43 loose↔packed — C 1:1 mirror (srmech_genome_explode / srmech_genome_pack). The rc150 Python loose↔packed surface now has its byte-for-byte C twin, keeping the genome C library 1:1 with Python.
- New
srmech_genome_explode(dir, out_dir, the_one, the_one_len, ws, ws_len)— writes one<out_dir>/<label>.chrper chromosome viasrmech_genome_export(the packed→loose half). Collects every label out of the obtained manifest FIRST (export reuses the workspace, so the manifest tree would be clobbered mid-loop), then exports each; a non-filename-safe label (/\\...) isSRMECH_ERR_BAD_INPUT. - New
srmech_genome_pack(loose_dir, dest, the_one, the_one_len, ws, ws_len)— enumerates*.chrinloose_dir(the one platform-specific touch: POSIXdirent/ Win32FindFirstFile, ifdef'd likesrmech_platform.c; no malloc, JPL Rule 3), peeks each bundle's innerdata.label, sorts by label (canonical order; UTF-8 byte order == code-point order, sostrcmpagrees with Python'sstrsort), thensrmech_genome_imports each in order. An empty dir / no.chrfiles isSRMECH_ERR_BAD_INPUT. - Byte-parity verified (WSL2 ctypes harness): C
explodeemits.chrbundles byte-identical to Pythongenome_explode, and Cpackemitsturns.bin+manifest.jsonbyte-identical to Pythongenome_pack— including the re-canonicalisation of a non-sorted source (insertion ordergamma/alpha/beta→ packedalpha/beta/gamma). - Standalone C smoke (
test_srmech_genome.c, 67/67): explode writesA.chr+B.chr; pack reproduces the sourceturns.binbyte-for-byte; both chromosomes window-addressable in the packed genome; empty-dir pack →BAD_INPUT. Pedantic-Werrorclean; JPL audit 6/6 (every new function ≤ 60 lines, ≥ 2 asserts; the directory scan carries an explicitguard < 65536over-bound, Rule 2). - No new Python callable —
describe()["tools"]["total"]stays 299. The genome C symbols are not ctypes-bound in_native.py, so adding them does not bump ABI (stays 3). 5 SSOT bumped.
[0.7.5rc150] - 2026-06-14¶
§43 file-management — loose↔packed (genome_explode / genome_pack; git's object model). A genome's turns.bin (the "packfile") can now be exploded into a directory of loose, content-addressed .chr bundles — one per chromosome — and packed back into a single genome. Composes the rc148 genome_export / genome_import keystone; the next §43 unit is the AMSC catalog.register_attested_root + per-chromosome descriptor.toml compose.
- New
genome_explode(path, out_dir, *, the_one=None)— writes one self-contained, MPR-attested.chrbundle per chromosome to<out_dir>/<label>.chr(the packed→loose half, likegit unpack-objects). Each.chrisgenome_export's self-verifying output, so the loose form is inspectable and shippable chromosome-by-chromosome. Returns a list of{label, path, region_sha256}dicts.the_one=explodes a manifest-less source (§44);ValueErroron a non-filename-safe label. - New
genome_pack(loose_dir, dest, *, the_one=None)—genome_imports every*.chrinloose_dirintodestin CANONICAL sorted-label order (the loose→packed inverse, likegit repack), so the packedturns.binis a well-defined function of the chromosome SET — insertion order is not preserved (a packed genome is canonicalised to sorted-label order). All bundles MUST share one coupling invariant (the_one); a mismatched.chris aGenomeBoundingError, a duplicate label aValueError; an empty dir aValueError. - Round-trip:
genome_pack(genome_explode(G))reproducesG'sturns.binANDmanifest.jsonbyte-identically whenGis already in canonical sorted-label order; for anyG, pack re-canonicalises while preserving every chromosome's bytes (verifiable per-chromosome withgenome_window). - Regression-pinned (
tests/test_genome_pack_explode_rc150.py, 8 cases): explode writes one.chrper chromosome; explode→pack byte-identical round-trip; pack canonicalises a non-sorted genome (content-preserving); mixed-the_one+ empty-dir + duplicate-label + unsafe-label guards; manifest-less explode; pack ignores non-.chrfiles. numpy-free. - Two new public callables (
genome_explode/genome_pack,non_computeRosetta bucket):describe()["tools"]["total"]297 → 299 (2 ToolEntries + 2 Rosetta-ledger rows + 7 count-tests bumped). ABI stays 3, numpy-free. The C 1:1 mirror is the planned follow-up. 5 SSOT bumped.
[0.7.5rc149] - 2026-06-14¶
§43 file-management — C 1:1 mirror of the chromosome .chr bundle (srmech_genome_export / srmech_genome_import). Completes the Python genome_export / genome_import (rc148) with their native peers, so the "tar one chromosome, ship it" surface is C/Python parity. GENOMEPLAN Stage 2 is now closed across both languages.
- New
srmech_genome_export(dir, label, out_path, the_one, the_one_len, ws, ws_len)— reads the chromosome's region (CHROM cap + coupled turns; the leading cap re-hashed against the manifestcap_sha256) + the_one and writes them toout_pathas ONE MPR record, built with the SAMEsrmech_jsonbuilder + writer the manifest uses — so the.chris byte-identical to the Pythongenome_export'sjson.dumps(sort_keys=True, ensure_ascii=False)+ LF.the_one=(NULL whenmanifest.jsonis present) exports from a manifest-less source (§44). - New
srmech_genome_import(chr_path, dest, the_one, the_one_len, ws, ws_len)— reads the.chr, RE-HASHES its region and the_one against the bundle's own attestation (self-verifying — a flipped byte isSRMECH_ERR_BAD_INPUT), then SEEDS a fresh genome atdest(region →turns.binverbatim) or APPENDS byte-for-byte into an existing dest (requiring the same coupling invariant — destthe_one.sha256== the.chr's — and a fresh label). The.chrregion/the_one are bounded bySRMECH_GENOME_CHR_REGION_MAX(1 MiB; the Python is unbounded — the C mirror bounds the.chrscratch like the body / manifest / chroms). - Verified C/Python 1:1: WSL2 ctypes byte-parity — the
.chrexport (incl. manifest-less), the SEED import, and the APPEND import all produceturns.bin+manifest.json+.chrbyte-identical to the Pythongenome_export/genome_import. Standalone C smoketest_srmech_genome.c59/59 pedantic-Werror; JPL Power-of-Ten 6/6 (≤ 60-line functions, ≥ 2 asserts, no goto/malloc/multi-line macro; the per-nibble hex decode is inlined intogenome_unhexto keep the Rule-5 ratchet tight). - ABI stays 3 (the genome C symbols are not bound in
_native.py—genome.pyis pure Python),describe()["tools"]["total"]stays 297 (no new Python callable this rc — the Python.chrsurface shipped in rc148), numpy-free. 5 SSOT bumped. §43 NEXT:genome_explode/genome_pack(loose↔packed) → compose the AMSCcatalog.register_attested_root+ per-chromosomedescriptor.toml.
[0.7.5rc148] - 2026-06-14¶
§43 file-management — the chromosome as a single bundleable, MPR-attested .chr file (UPSTREAM GENOMEPLAN Stage 2). Now that §44 made the strand self-describing and §45 made it editable in place, a chromosome can be EXPORTED as one self-contained, content-addressed file, shipped, and re-IMPORTED into a genome self-verifying — the "tar one chromosome, ship it" goal. This is the Stage 2 keystone (export/import); explode/pack + the AMSC catalog/descriptor compose follow.
- New
genome_export(path, label, out, *, the_one=None)— reads the chromosome's fixed-width region (CHROM cap + coupled data turns; the cap re-hashed against the manifestcap_sha256) and writes it — withthe_one— tooutas ONE MPR record (MPR v1;response_sha256IS the region hash). Composessrmech.amsc.format(theMPRRecord+sha256_bytescontent-address) — NOT a parallel attestation.ValueErroron a missing label;the_one=exports from a manifest-less source (§44). - New
genome_import(chr_path, dest, *, the_one=None)— reads the.chr, RE-HASHES its region andthe_oneagainst the bundle's own attestation (self-verifying — a flipped byte is aGenomeBoundingError). Ifdesthas no genome yet the.chrSEEDS a fresh one (its region becomesturns.binverbatim); ifdestalready holds a genome the chromosome is APPENDED byte-for-byte — which REQUIRES the same coupling invariant (the_onematch) and a fresh label. The manifest is re-derived by scanning the grown body (§44). - A
.chris byte-stable (the canonicaljson.dumps(sort_keys=True, ensure_ascii=False)+ LF the manifest uses) and round-trips byte-identically: the seeded/appendedturns.binequals the exported region verbatim. - Regression-pinned (
tests/test_genome_chr_bundle_rc148.py, 8 cases): the.chris a valid MPR-v1 record tagged as a chromosome bundle withresponse_sha256 == region hash; export→import SEEDS a byte-identical genome; export→import APPENDS byte-for-byte (samethe_one); a tampered region self-verifies toGenomeBoundingError; thethe_one-mismatch + duplicate-label guards; manifest-less export. numpy-free. - Two new public callables (
genome_export/genome_import,non_computeRosetta bucket):describe()["tools"]["total"]295 → 297 (2 ToolEntries + 2 Rosetta-ledger rows + 7 count-tests bumped). ABI stays 3, numpy-free. C 1:1 mirror +genome_explode/genome_pack(loose↔packed) + the AMSCcatalog.register_attested_rootcompose are the planned §43 follow-ups. 5 SSOT bumped.
[0.7.5rc147] - 2026-06-14¶
§45 in-place genome edit — C 1:1 mirror of the rc146 genome_remove / genome_replace. rc146 made the PYTHON in-place edits a pure byte-splice on the §44 self-describing body; this brings the C surface to parity so the on-disk turns.bin + manifest.json are byte-identical whether the edit ran in Python or C.
- New
srmech_genome_remove(dir, label, the_one, the_one_len, ws, ws_len)+srmech_genome_replace(dir, label, region, region_len, leaf_dim, the_one, the_one_len, ws, ws_len)insrmech_genome.c— each obtains the manifest (parse if present, else §44 rebuild-by-scan), locates the chromosome's[byte_offset, byte_len)viagenome_find_chrom, bound-checks the whole body againstbody_sha256(new sharedgenome_read_bound_body, factored out ofgenome_grow_body), splices the span out (remove) / out-and-in (replace) IN PLACE in the thread-localgenome_body_scratch, then re-saves viasrmech_genome_save(which re-derives the manifest by scanning).removerejects the genome's only chromosome / a missing label;replacetakes the pre-coupled region bytes (the caller couplesleavesthroughthe_one, mirroring the Python). Like APPEND (a write op)the_oneis REQUIRED (the manifest the_one hash+hex). - Signatures (
c/include/srmech.h): two additive symbols + doc comments. The genome C symbols are NOT ctypes-bound in_native.py(Pythongenome.pyis pure-Python), so this has no Python ABI surface — ABI stays 3,describe()["tools"]["total"]unchanged at 295 (no new Python callable), numpy-free. - Verified. Reshaped C smoke (
test/test_srmech_genome.c) now 40/40 under pedantic-Werror— incl. a §45 section: middle/first-chromosome excise is a pure byte splice (survivors verbatim), the replace span-swap (neighbours byte-identical), the only-chromosome / missing-labelBAD_INPUTguards, and the corrupt-body integrity bound firing before an edit. JPL ratchet 6/6 (genome_read_bound_body/srmech_genome_remove/srmech_genome_replaceeach ≥ 2 asserts, ≤ 60 lines, no goto/malloc/multi-line macro). WSL2 ctypes byte-parity confirmed:srmech_genome_remove/_replaceproduceturns.bin+manifest.jsonbyte-for-byte identical to the Pythongenome_remove/genome_replace.JPL_AUDIT.mdupdated. 5 SSOT bumped. The §45 surface is now Python+C parity — GENOMEPLAN Stage 1 closed across both languages.
[0.7.5rc146] - 2026-06-14¶
§45 IN-PLACE genome edit — biology excises, it does not re-synthesize (UPSTREAM GENOMEPLAN Stage 1). Now that §44 (rc144 Python / rc145 C) made the loaders reconstruct the catalog by scanning turns.bin (the strand is the SSoT, manifest.json an optional .fai cache), editing a genome is a pure BYTE-level splice on the self-describing body — no kernel is decoded or re-coupled, so the surviving chromosomes' coupled bytes stay byte-identical (only relocated). This is the next step on the bottom-up critical path (0b §44 ✓ → 1 §45 in-place edit → 2 §43 bundling → 3 domain silos).
- New
genome_remove(path, label, *, the_one=None)— finds the chromosome's[byte_offset, byte_offset+byte_len)region in the body (§44 scan), splices THAT span out ofturns.bin, and re-derives the optional manifest by scanning the spliced body (body_sha256/n_turns/ every survivor'sbyte_offsetrecomputed). The whole on-disk body is re-hashed against the committedbody_sha256BEFORE the edit (never splice a corrupt body —GenomeBoundingError).the_one=is needed only whenmanifest.jsonis absent (its length is the leaf width for the §44 rebuild-by-scan). RaisesValueErroron a missing label or on removing the genome's only chromosome. - New
genome_replace(path, label, leaves, the_one)— splices the chromosome's old span out and a FRESH telomere-cappedchromosome(leaves, the_one, label)IN at the same position; the OTHER chromosomes' bytes are untouched (in-place edit, NOT a whole-genome re-pack).the_oneis required (it re-couples the new leaves AND supplies the leaf width) and must matchleaf_dim. - Both route through a shared
_write_body_and_manifesthelper that rebuilds-by-scan FIRST (so a bad splice raises before either file is touched) then commitsturns.bin+ the re-derived.faimanifest. The new body is the survivor spans concatenated verbatim — proving the edit is genuinely in-place rather than a decode/re-encode re-pack. - Regression-pinned (
tests/test_genome_in_place_edit_rc146.py, 12 cases): the splicedturns.bin== the survivor spans concatenated EXACTLY (middle + first-chromosome excise; the in-place replace span-swap); survivors reload byte-for-byte; manifest-less remove rebuilds the.fai; the tar-just-turns.bin-after-edit reload; remove→append round-trip; the corrupt-body integrity bound; and theValueErrorguards (only-chromosome / missing-label / wrongthe_onedim). numpy-free. - Two new public callables (
genome_remove/genome_replace,non_computeRosetta bucket — IO, no compute kernel):describe()["tools"]["total"]293 → 295 (2 ToolEntries + 2 Rosetta-ledger rows + 7 count-tests bumped). ABI stays 3 (genome C is not ctypes-bound). C 1:1 mirror is the planned follow-up (rc147). 5 SSOT bumped.
[0.7.5rc145] - 2026-06-14¶
C 1:1 mirror of the rc144 §44 manifest-optional loaders — srmech_genome_* reconstruct from turns.bin alone too. rc144 made the PYTHON disk loaders treat manifest.json as an optional .fai cache (rebuild-by-scan when absent); this brings the C surface to parity so a genome shipped as turns.bin ALONE loads in C as well as Python.
- New
genome_obtain_manifest(dir, the_one, the_one_len, ws, ws_len, &out)insrmech_genome.c— parsesmanifest.jsonwhen present (the cheap path;turns.binuntouched), else REBUILDS the catalog by scanning the self-describing body (reusing the rc143genome_scan_chroms+genome_fill_strings, then a newgenome_build_manifest_treethat returns the MPRRecord tree in the arena — the loaders' accessors walk it identically to a parsed one). The rebuild needsthe_one(the_one_lenISleaf_dim, the width the body does not carry inline); a missing manifest withthe_one==NULLreturnsSRMECH_ERR_BAD_INPUT(the helpful error, not a bare IO miss).genome_build_manifestis refactored to call the new tree-builder then serialise — save output stays byte-identical. - Signatures (
c/include/srmech.h):srmech_genome_catalog/_load/_windowgain athe_one/the_one_lenpair (mirroring rc144'sthe_one=kwarg);srmech_genome_appendalready had it and now routes through the rebuild too. All four loaders go throughgenome_obtain_manifest; passthe_one=NULL, 0when a manifest is known present. - Verified. Reshaped C smoke (
test/test_srmech_genome.c) now 27/27 under pedantic-Werror— incl. a §44 section that deletesmanifest.jsonthen catalogs / loads / windows / appends manifest-less + asserts the helpfulBAD_INPUTwhen nothe_one. JPL ratchet 6/6 (the newgenome_obtain_manifest+genome_build_manifest_treeeach carry ≥ 2 asserts, ≤ 60 lines). WSL2 ctypes save byte-parity re-confirmed (turns.bin+manifest.jsonbyte-identical to Python after the refactor) — which transitively proves the C rebuild matches Python's, since both reuse the same tree builder. - No ABI / tool-count change. The genome C symbols are not ctypes-bound in
_native.py(Pythongenome.pyis pure-Python), so thesrmech_genome_*signature changes have no Python ABI surface — ABI stays 3.describe()["tools"]["total"]unchanged at 293, numpy-free.JPL_AUDIT.mdupdated for the new helpers. 5 SSOT bumped.
[0.7.5rc144] - 2026-06-14¶
manifest.json is now the OPTIONAL .fai cache §44 promised — the disk loaders reconstruct from turns.bin alone (UPSTREAM §44 follow-up). §44 (rc142/rc143) made the body self-describing and the manifest exactly rebuildable, but the loaders still HARD-REQUIRED manifest.json — delete it and genome_load threw FileNotFoundError. This closes that gap: every loader now treats the strand as the SSoT and the manifest as a rebuildable sidecar, so a genome can be shipped as turns.bin ALONE (the §43 "tar one chromosome bundle" goal).
- New
_catalog_data(path, the_one=None)indirection. Whenmanifest.jsonexists, the loaders read it (the cheap, unchanged fast path —turns.binnever opened for a catalog read). When it is ABSENT, the catalog is REBUILT by scanning the self-describing fixed-width body (_rebuild_manifest_from_bodywalks the inline CHROM/GENE caps + accumulates byte offsets /cap_sha256/ data-turnleaf_count/body_sha256). The rebuild needsthe_one(its length ISleaf_dim, the block width the body does not carry inline), so a manifest-less load with nothe_one=raises a helpfulGenomeBoundingError(not a bareFileNotFoundError). genome_load/genome_window/genome_genes/genome_catalog/genome_appendall manifest-optional.genome_window+genome_cataloggain an optionalthe_one=kwarg (the HV is not MCP-coercible, so — likegenome_load/genome_genes— it stays out of the ToolEntry parameter list);genome_load/genome_genesalready had it. The rebuilt catalog is byte-for-byte identical to the saved manifest data, sogenome_appendregenerates a fresh.faicache after a manifest-less append.- Regression-pinned (
tests/test_genome_manifest_optional_rc144.py, 8 cases): the rebuilt-by-scan catalog == the saved manifest EXACTLY; whole + subset + window + genes loads all work manifest-less and equal their with-manifest results; the helpful error (notFileNotFoundError) when no manifest + nothe_one; the tar-just-turns.bin-then-load flow; and a manifest-lessgenome_appendthat rebuilds + re-writes the cache. The full genome + registry-gate suite stays green (123 in the slice). - No new callable / no surface change: signature-only (two optional
the_one=kwargs).describe()["tools"]["total"]unchanged at 293; thegenome_catalogToolEntry summary reworded (its "never opens turns.bin / no body bytes" claim was conditionally false under the rebuild path). ABI stays 3, numpy-free. C 1:1 mirror is the planned follow-up (the Csrmech_genome_load/_catalog/_windowreadmanifest.jsontoo;genome_scan_chromsalready exists to back the rebuild). 5 SSOT bumped.
[0.7.5rc143] - 2026-06-14¶
C 1:1 mirror of the §44 self-describing genome persistence (F733) — completes the Python-first rc142 reshape. rc142 shipped the §44 strand-is-SSoT redesign on the Python side; this rc reshapes srmech_genome.c to match, so the C srmech_genome_save produces a manifest.json + turns.bin byte-for-byte identical to Python's genome_save while deriving the manifest by scanning the inline caps (no caller-supplied chromosome layout). The on-disk format is the SSoT; both implementations agree at the byte level.
- C header (
c/include/srmech.h).SRMECH_GENOME_FORMAT_VERSION1 → 2; newSRMECH_GENOME_CHROM_CAP_MARKER(0x43) /SRMECH_GENOME_GENE_CAP_MARKER(0x47); thesrmech_genome_chrom_tcaller-layout struct is removed.srmech_genome_save(dir, body, body_len, leaf_dim, the_one, the_one_len, ws, ws_len)drops thechroms/n_chromsparams — the body self-describes. - C source (
c/src/srmech_genome.c). Newgenome_decode_label(reads a NUL-terminated label out of a cap leaf) +genome_scan_chroms(walks the fixed-width blocks: a CHROM cap opens a chromosome + hashes the cap + decodes the label + recordsbyte_offset;byte_lenaccrues per block;leaf_countcounts DATA turns only — CHROM + GENE caps excluded).genome_savecalls the scan instead of consuming a caller layout;genome_appendchecks the new label for a duplicate then re-saves the grown body. The §41 manifest writers are unchanged. New helpers each carry ≥ 2 asserts and are ≤ 60 lines (JPL Rules 4 + 5 ratchet green, 6/6). - Byte-parity verified. A WSL2 ctypes harness builds a multi-gene §44 genome with Python srmech (numpy-absent), then calls C
srmech_genome_saveon the same body bytes:turns.bin(1152 B) andmanifest.json(1748 B) are byte-identical, the C deriving the manifest entirely by scanning the inline caps. The standalone C smoke (test/test_srmech_genome.c, reshaped to §44 packed caps) passes 18/18 under pedantic-Werror. - No tool-count change / no ABI change. The genome C symbols are not ctypes-bound in
_native.py(Pythongenome.pyis pure-Python), so thesrmech_genome_savesignature change has no Python ABI surface — ABI stays 3.describe()["tools"]["total"]unchanged at 293, numpy-free.JPL_AUDIT.mdupdated for the renamed helpers. 5 SSOT bumped.
[0.7.5rc142] - 2026-06-14¶
Genome multi-gene persistence redesigned as a SELF-DESCRIBING body (F733 / UPSTREAM §44) — supersedes rc141's manifest gene-index sidecar. rc141 recorded each chromosome's gene boundaries in the manifest (a sidecar offset-table). §44 corrects the direction: biology has NO offset table — chromosome + gene boundaries are FIXED-WIDTH INLINE markers scanned for in the strand itself (TTAGGG telomere repeats / stop codons), and fixed-width records are exactly what make the sidecar unnecessary. So the strand (turns.bin) becomes the SSoT and the manifest an optional, rebuildable .fai-style cache. This is a breaking on-disk change (GENOME_FORMAT_VERSION 1 → 2); pre-1.0, no shipped on-disk-genome consumers.
- Inline fixed-width caps. A cap leaf is now
[marker] + label, NUL-padded toleaf_dim:CHROM_CAP_MARKER(0x43) opens a chromosome,GENE_CAP_MARKER(0x47) opens an intra-chromosome gene. The first byte is a marker> 3, so a cap is scanned-for (never mistaken for a Klein-4{0,1,2,3}data turn) and its label is recovered inline (_unpack_cap) — the strand self-describes, no label set needed.telomere(label, dim)now returns a CHROM cap (wasklein4_randomof a label hash, bytes0..3— NOT scan-recognisable, which is what forced the sidecar);_gene_capreplaces the §43 TLV_gene_header.GENE_FRAME_TAGkept as a back-compat alias. - In-memory surface.
recall/genesskip / decode caps by their inline marker (droptlv_unpack).genome(chromosomes=[(label, [(gene_label, gene_leaves), …]), …], the_one)now returns ONE self-describing strand (NOgene_index, no 2-tuple).partition(strand, the_one, labels=None)SCAN-discovers the chromosomes + inline labels (thelabelsarg is now optional — a filter when given). - Persistence.
genome_save(strand, path, the_one, labels=None)drops thegene_index=param; it scans the strand's CHROM caps, writes the self-describing body, and writes the manifest as a DERIVED cache.leaf_countnow counts DATA turns only (excludes CHROM + GENE caps)._hv_from_blockis sector-aware (capssectors=256, data turnssectors=QUAD).genome_genes(path, label)pages the chromosome region and SCANS it for inline GENE caps (reusing the in-memorygenes()), raising on a single-kernel chromosome; there is no gene-index sidecar to read.genome_windowskips every cap (a multi-gene chromosome flattens to its data turns).genome_load/genome_genesgain athe_one=override (the manifest the_one is now an optional cache). - Regression-pinned (
tests/test_genome_multigene_persist_rc142.py, 6 cases — replaces the rc141 sidecar test): the single self-describing strand (no 2-tuple), the inline-cap disk round-trip via scan (genome_genes== in-memorygenes()== originals), the body carrying the caps + the manifest carrying NO sidecar, the single-kernel refusal, and partition/window flattening.test_genome_genes_rc134updated to the §44 cap invariant (CHROM + GENE caps both marker-led; only data turns stay Klein-4). The full genome + registry-gate suite stays green (175 passed): tool-schema coverage + the #928 Rosetta ledger + thedescribe()counts. - No tool-count change: signature changes only — the rc141
gene_index=param is REMOVED, no new callable.describe()["tools"]["total"]unchanged at 293; thegenome_save+genome_genesToolEntry summaries reworded for §44 (genome_savelabelsnow optional). ABI stays 3, numpy-free. C 1:1 mirror is reshaped by §44 (the manifest-writing role shrinks to optional-derived; the C body-format gains the inline caps) — the planned follow-up after the structure is re-validated in the research environment. 5 SSOT bumped.
[0.7.5rc141] - 2026-06-13¶
Genome multi-gene PERSISTENCE (F732 / UPSTREAM §43.1) — Python side first. rc134 shipped the in-memory multi-gene chromosome (chromosome(genes=…) + genes()); this closes the DISK round-trip. Manifest-gene-index design (chosen over a body-format change): turns.bin stays byte-identical to the flattened single-kernel genome — no GENOME_FORMAT_VERSION bump, the fixed-width 0..3 body invariant is untouched, the gene boundaries live only in the manifest. Python side ships first (this rc) so the multi-gene surface can be exercised in the research environment before the C 1:1 mirror lands — if the structure needs to change, it changes here, cheaply, before the byte-parity work.
genome(chromosomes=[(label, [(gene_label, gene_leaves), …]), …], the_one)→(pure_strand, gene_index): flattens each chromosome's genes into one telomere-capped region (NO gene-header turns in the body, unlike the in-memorychromosome(genes=…)), withgene_index = {label: [(gene_label, leaf_count), …]}. The single-genegenome(kernels, the_one)form is unchanged (returns a flat strand).genome_save(strand, path, the_one, labels, *, gene_index=None)— records each multi-gene chromosome's gene boundaries as an optional"genes": [[gene_label, leaf_count], …]manifest field (_build_manifest_data); the fixed-width body write is unchanged; a leaf-count-tiling integrity check (the genes must tile the chromosome's storedleaf_countexactly). Single-kernel chromosomes omit the field (on-disk back-compat: a single-kernel genome'smanifest.json+turns.binare byte-identical to before).genome_genes(path, label)— the disk counterpart ofgenes(): pages the chromosome window (genome_window, RAM-bounded + cap-integrity-checked) +quad_turn-uncouples through the manifest'sthe_one+ slices by the gene index →[(gene_label, gene_leaves), …], exactly matchinggenes(chromosome(genes=…))in memory. Fail-loud on a single-kernel chromosome (no index) and on a manifest/body leaf-count disagreement (GenomeBoundingError).- Regression-pinned (
tests/test_genome_multigene_persist_rc141.py, 9 cases): the(strand, gene_index)builder + pure-strand, the body-identical-to-flattened invariant (proves no body-format change),genome_genes== in-memorygenes()== the originals, the optional manifest field (absent for single-kernel), partition/window/load still working on a multi-gene chromosome, and three fail-loud guards. The 50 existing genome tests stay green (no regression). genome_genesis a NEW publicsrmech.amsc.*callable — added atool_schema.pyToolEntry + arosetta_classification.ndjsonline (composition_of_c);describe()["tools"]["total"]292 → 293 (the five count-tests bumped).genome(chromosomes=)/genome_save(gene_index=)are existing functions gaining kwargs (not new callables). ABI stays 3, numpy-free. C 1:1 mirror is the planned follow-up (srmech_genome.cmanifestgenesemit + WSL2 byte-parity). 5 SSOT bumped.
[0.7.5rc140] - 2026-06-13¶
SedenionRegister converted to a config-driven [class] TOML (sedenion_register.toml) — the first rich multi-field / dict-state / chain / self-returning conversion. Per [feedback_prefer_config_driven_toml_classes], the make_class contract extensions rc137 (dict fields + mutates) + rc139 (chain + returns = "self") exist exactly so this harder domain object (UPSTREAM §31 / PR #687, F465 + F468) becomes declarative — completing the make_class arc proof from the immutable One (rc138) up to a stateful storage instrument.
- Flat cascade-op adapter layer (
srmech/amsc/cascade/sedenion_register.py):sed_write/sed_materialize/sed_read_unbind/sed_clean/sed_slots/sed_couple_working/sed_uncouple_working/sed_carry/sed_correct/sed_navmap/sed_navigate/sed_is_navigable— each rehydrates a transientSedenionRegisterfrom the declarativeD/codebook/slotsfields (the private_minter/_addr_cachecaches are dropped — minting is deterministic from(name, D)) and delegates to the existing method. No logic duplication; the TOML class is byte-identical to the Python class. - Packaged
sedenion_register.toml(srmech/amsc/_research/class_catalog/,kind = "storage"): exercises the full routing contract —write→mutates = ["slots", "codebook"];read→ a visible 2-stagechain(sed_read_unbind→sed_clean: the unbound noisy vector is the intermediate, cleaned against the codebook);navigate→returns = "self"(a fresh routed register,selfuntouched);materialize/slots/couple_working/uncouple_working/carry/correct/navmap/is_navigable→ single ops. Ships A-tier alongsidegenome.toml/one.toml/hurwitz.toml, soSedenionRegistersurfaces inlist_classes()/describe_class(...)/ thesrmech classCLI / MCP with zero user Python. - DSL-class-vs-Python equivalence pinned (
tests/test_sedenion_register_class_catalog_rc140.py, 9 cases): for the sameD, everymake_class("SedenionRegister")method returns exactly what the Python register returns —slots/codebookafterwrite(mutated in place, byte-identical mint), thematerializebundle bytes, the 2-stagereadchain (incl. the empty-register short-circuit + the Class-C sign),navigate(fresh instance +selfuntouched + reads-back-routed-content), andcouple_working/uncouple_working/carry/correct/navmap/is_navigable; plus thedescribe_classrouting surface (mutates/chain/returns). - No tool-count change: the
sed_*adapters are reachable only through the class surface (structuredslots/codebook/Dargs have no MCP coercer), exempt in the tool-schema coverage gate + classified in the Rosetta ledger (composition_of_c, the structuralsed_slotsnon_compute) exactly like theone.one_*accessors. ABI stays 3, numpy-free, native build holds no libm;describe()["tools"]["total"]unchanged at 292 (classes are not tools). 5 SSOT bumped.
[0.7.5rc139] - 2026-06-13¶
Config-driven TOML classes (make_class): the contract's last two method primitives — chain (a visible multi-op pipeline) + returns = "self" (a self-returning method). rc137 gave single-field mutates + dict fields; rc139 completes the method-routing contract so the hard domain classes (a register whose materialize/read are multi-op and whose navigate yields a new instance — the upcoming SedenionRegister → sedenion_register.toml) become declarative TOML.
chaindirective (srmech/dsl/_class_catalog.pyCatalogClass._run_chain): a[class.method.X]may declarechain = [{op, binds, as}, …]instead of a singleop(mutually exclusive). Each stage resolves itsbindspositionally from (1) a prior stage'sasresult, then (2) the call kwargs, then (3) the instance fields; non-(op/binds/as) keys are static stage kwargs; the method returns the last stage's result. The cascade composition is declared in the TOML (the config-driven point), not buried inside one flat op. (The linearsrmech.dsl.Chainthreads a single field-free value; a class method needs per-stage field binds, so this is the class-aware engine.)returns = "self"directive (CatalogClass._apply_returns): a self-returning method — the op/chain returns the new instance's{field: value}state-dict and a FRESH same-class instance is constructed from it;selfis untouched (thenavigate(j) -> new registershape). Validates the result is a dict whose keys ⊆ declared fields; fail-loud otherwise.- Routing widened to 4-way exclusivity: at most one of
appends/sets/mutates/returns; and exactly one op-source (oporchain).describe_classsurfaceschain(stage list) +returns; the §39generate_class_descriptoremitter round-trips both (chainas an inline-table array, the widened guards). - Regression-pinned (
tests/test_class_catalog_chain_returns_rc139.py, 11 cases): the chain pipeline (prior-stage threading + static stage kwargs + field/call binds + unresolvable-bind fail-loud), the self-return (fresh instance, self unchanged, chainable, bad-return-shape + undeclared-field guards),op/chainandsets/returnsexclusivity,describe_classsurfacing, and thegenerate_class_descriptorround-trip. The Genome / One / Hurwitz / mutates classes stay green. - Additive only — a pure DSL contract extension (no new public
srmech.amsc.*callable): ABI stays 3, numpy-free, native build holds no libm;describe()["tools"]["total"]unchanged at 292 (no ToolEntry / Rosetta-ledger churn). 5 SSOT bumped. Next:SedenionRegister → sedenion_register.tomlusing dict + mutates + chain + returns.
[0.7.5rc138] - 2026-06-13¶
"The One" S(σ,θ) converted to a config-driven [class] TOML (one.toml) — the first substrate object declared declaratively over the shipped cascade. Per [feedback_prefer_config_driven_toml_classes], the immutable accessor-shaped One (#887) is the clean conversion target: it fits the rc137 make_class contract with no new directive (a single one field holding the constructed S(σ,θ); each method a one-bind cascade-op ref). This proves the config-driven class pattern end-to-end on the substrate generator itself.
- Flat cascade-op accessor layer (
srmech/amsc/cascade/one.py):one_dim/one_imag_dims/one_partition/one_plane_counts/one_grammar_slots/one_flat_rational/one_matrix— the genome two-layer pattern (ship eachOneaccessor as a module-level flat op, then bind it in TOML). Each takes the constructedOneas its sole bind, mirroring the existing module-levelto_scalar(one, …). - Packaged
one.toml(srmech/amsc/_research/class_catalog/): the[class] One(kind = "substrate") with 8 methods —dim/imag_dims/partition/plane_counts/grammar_slots/flat/matrix(bound to the new flat ops) +scalar(bound tosrmech.amsc.cascade.to_scalar). Ships A-tier alongsidegenome.toml, soOnesurfaces inlist_classes()/describe_class("One")/describe()["classes"]/ thesrmech classCLI / MCP — zero user Python to use it:make_class("One")(one=the_one(+1, 0, 1)).partition(). - DSL-class-vs-Python equivalence pinned (
tests/test_one_class_catalog_rc138.py): for the samethe_one(σ, θ), everymake_class("One")method returns exactly what the PythonOne's accessor returns — across θ=0 and a non-trivial θ, both chiralities — incl. the 14 exact rationals bit-for-bit, the 14×14 numpy-freeMatentry-for-entry, and thescalarprojection family; plus the n=1-is-σ-only structural prediction surviving the TOML-class path. - No tool-count change: the accessor ops are reachable only through the class surface (a structured
Onearg has no MCP coercer), exempt in the tool-schema coverage gate exactly liketo_scalar. ABI stays 3, numpy-free, native build holds no libm;describe()["tools"]["total"]unchanged at 292 (classes are not tools). 5 SSOT bumped.
[0.7.5rc137] - 2026-06-13¶
Config-driven TOML classes (make_class): richer contract — dict-typed fields + multi-field state routing (mutates). Per the [feedback_prefer_config_driven_toml_classes] direction, new srmech domain objects should be declared as [class] TOML descriptors consumed by srmech.dsl.make_class, not hand-coded Python. The genome seed proved the contract over single-field state routes (appends = list.append, sets = replace one field) on None-/list-typed fields. Converting a richer object (e.g. a register whose write touches a codebook and a slot-map at once) needs two more primitives; this rc adds them — the contract groundwork for the upcoming One → one.toml conversion and the harder multi-field-state classes.
dict-typed fields (srmech/dsl/_class_catalog.py_field_default): a[class.field]declared"dict"now defaults to a fresh{}at construction (peer to the existing"list"→[]); each instance gets a distinct dict (no shared-mutable-default aliasing).mutates— multi-field state routing (CatalogClass._invoke/ new_apply_mutates): a method may declaremutates = ["fieldA", "fieldB"]; its bound op returns(return_value, {field: new_value}), each named field is replaced, and the barereturn_valueflows back to the caller. Fails loudly on a non-(value, dict)op return, on an update touching a field outside the declared set, and on amutatesnaming an undeclared field.appends/sets/mutatesare mutually exclusive (enforced at invoke).- Introspection in lockstep (
srmech/dsl/_class_surface.py):describe_class(...)["methods"][m]surfaces"mutates"(a field-name list) when present, and thegenerate_class_descriptor§39 emitter round-tripsmutates = [...]with the same 3-way exclusivity guard — so an introspect → emit → re-parse stays faithful for the richer contract. - Regression-pinned (
tests/test_class_catalog_mutates_rc137.py): dict-field default + per-instance isolation, the two-field mutate threading state across calls,describe_classsurfacing, thegenerate_class_descriptorround-trip, and all four fail-loud guards — exercised against a synthetic ops module bound by dotted path (no new shipped op). - Additive only — no ABI change (ABI stays 3), numpy-free, native build holds no libm;
describe()["tools"]["total"]unchanged at 292 (a DSL contract extension, not a new AMSC ToolEntry). 5 SSOT bumped.
[0.7.5rc136] - 2026-06-13¶
SSoT: the srmech bus CLI now derives the connection-secret keyword from the live API signature instead of hardcoding it. rc135 renamed the bus pre-shared-secret kwarg seed= → dna= and had to fix it in two hand-maintained places — srmech.bus.connect/serve and the CLI, which spelled the keyword itself (and broke when the API renamed it). This rc removes that duplication: the CLI asks the API what its secret keyword is.
- New
srmech.bus.secret_kwargs(secret) -> dict(srmech/bus/_params.py): returns theconnect()/serve()keyword arguments carrying a resolved per-channel pre-sharedsecret, keyed by whatever the liveconnectsignature names that parameter — identified by role (the soleOptional[bytes]keyword), resolved viatyping.get_type_hints(PEP-563-safe). Returns{}forNone, so callers always splat:connect(name, **secret_kwargs(secret)). Fails loudly (no silent guess) if the secret keyword is ambiguous or absent. srmech/cli/bus.pyrepointed: all 5connect(…, dna=…)/serve(…, dna=…)call sites (tap/pipe×2 /send/serve) now use**_bus.secret_kwargs(…). The CLI no longer contains the secret keyword as a literal — a future rename of the API parameter propagates to the CLI with zero edits. The CLI's user-facing--seedflag +_seed.pydiscovery cascade (SRMECH_BUS_SEED/~/.srmech/bus-{name}.seed) are unchanged.- Regression-pinned (
tests/test_bus_secret_kwarg_ssot.py): derivation is keyed to the liveconnect/serve/aiosignatures; a simulated rename is followed automatically; ambiguous/missing secret raises; and a ratchet asserts the CLI source never hardcodes the keyword again. - Additive only — no ABI change (ABI stays 3), numpy-free, native build holds no libm;
describe()["tools"]["total"]unchanged at 292 (secret_kwargsis a bus helper, not an AMSC ToolEntry). 5 SSOT bumped.
[0.7.5rc135] - 2026-06-13¶
Carrier consolidation (#564 follow-up): collapse 11 redundant Class-L matrix ops into 3 dtype-polymorphic mat_* ops, hard-remove the overdue v0.5.0 bus shims, and bring the C surface back to 1:1 with Python — fix the numpy-removal duplication debt. The rc69–rc134 numpy-removal arc was meant to be numpy-spirited (one dtype-transparent carrier op per operation), but each per-module flip added a kernel, leaving the same operation in two-to-four forms. The redundant surface is hard-removed. (This is a breaking cleanup; per user direction the version-jump decision is deferred to the next live-PyPI cut, so it ships on the 0.7.5rcN line.)
- Removed 11 ops from
srmech.amsc.laplacian:dense_matmul_{real,complex},dense_matvec_{real,complex},dense_dot_{real,complex},dense_norm,dense_outer_{real,complex},mat_dot_{real,complex}(the dtype-split_real/_complexpairs + the superseded loose-inputdense_*generation). - Added 3 dtype-polymorphic
mat_*ops (peers of the existingmat_matmul/mat_norm/mat_solve/ …):mat_dot(unifies the 4 dot forms),mat_matvec(column-Matovermat_matmul),mat_outer— each returns float for real operands, complex for complex. The carrier@/·idiom (Mat/Vec) and thehdcloop ops were repointed onto the unified surface;mat_norm/mat_dotnow flatten nested-list matrices (parity with the olddense_norm). - Verify-first: proved the unified ops are value-identical to the 11 they replace (real + complex, list + carrier) before deleting anything.
- C/Python 1:1 parity (user directive): the now-orphaned native kernel
srmech_dense_matvec_complexis removed from the C surface — matvec is a composition overmat_matmulon the Python side, so the dedicated C kernel had no caller. Dropped fromc/include/srmech.h(prototype + doc),c/src/srmech_laplacian.c(definition + the ADR-0002-Phase-2 doc bullet), and the_native.pyctypes binding.srmech_dense_matmul_complexstays (it still backsmat_matmul). Removing a dead symbol does not bump ABI (stays 3; the ctypes shim binds viahasattr). - Overdue v0.5.0 bus shims hard-removed ("check other aliases left over"):
srmech/bus/_chain.py(the rc3 SHA-256-state cipher deprecation shim, "removed in v0.5.0 final") is deleted, and theseed=deprecated kwarg is removed frombus/_client.py/_server.py/aio.py(theseed → dnarename completes; the cipher surface is Bio-TOTP / UTLP Claim 255). The separate_seed.pyclient-secret discovery mechanism (SRMECH_BUS_SEEDenv +~/.srmech/bus-{name}.seed) is unrelated and KEPT.test_bus.py(3 deprecation tests) +test_bus_aio.pyupdated. - Fixed:
hermitian_eigendecomposeZeroDivisionErroron a DEGENERATE eigenvalue in the pure-Python fallback (surfaced numpy-free + native-absent; the minimal trigger is the identity matrix). The pure-Python complex-Hermitian path uses the2nreal-symmetric embeddingM = [[A,-B],[B,A]], which doubles each complex eigenvector via the J-rotationi·z— so the "every other column" pick collapsed to a zero vector after same-eigenvalue Gram–Schmidt, then1/‖0‖. The fallback now scans the embedding columns at the same eigenvalue for an independent reconstruction (the eigenspace always has more directions than accepted). Native-path-unaffected (the Csrmech_hermitian_eigendecomposeuses direct complex-Jacobi rotations, which stay orthonormal across degeneracy by construction — so no C change / no parity drift); regression-pinned (test_hermitian_eigendecompose_degenerate_numpy_free: unitary basis + exactV·diag(λ)·Vᴴ = Hon identity + two degenerate Hermitians). Noabs()(Class-K via_rsqrtof squares). - Additive/subtractive ABI (no wire-format change; ABI stays 3), numpy-free;
describe()["tools"]["total"]300 → 292 (−11 ToolEntries + 3); the #928 Rosetta ledger + 7 duplicated count-tests updated. 5 SSOT bumped.
[0.7.5rc134] - 2026-06-13¶
Genome file-management increment #1 (F730 / UPSTREAM S43): several genes per chromosome — tlv.tlv_unpack + genome.chromosome(genes=…) / genome.genes(). The genome reads like a library → tarballable chromosome → framed genes; this rc adds the intra-chromosome level: many genes inside one telomere-capped chromosome, each gene a Class-B tlv_pack frame (the cheaper internal delimiter; the telomere stays the chromosome boundary cap). Composed from existing AMSC — compose, don't reinvent (the research-env reuse audit mapped 13/13 requirements to existing ops).
tlv.tlv_unpack(buffer, offset=0) → (tag, value, next_offset)— the missing inverse oftlv_pack(the writer shipped; the reader did not). Feednext_offsetback in to walk a concatenation of frames; exact round-trip; raises on a truncated prefix or a length running past the buffer. (Rosettanon_compute— a byte-frame reader, no numeric kernel; its C twin is a tracked Rosetta follow-up.)genome.chromosome(genes=[(label, leaves), …], the_one)— pack several genes into ONE telomere-capped strand, each gene introduced by a tlv gene-headerHV. The single-kernelchromosome(leaves, the_one)path is unchanged (pass exactly one ofleaves=orgenes=).genome.genes(strand, the_one) → [(label, leaves), …]— the inverse reader. A gene-header's first byte isGENE_FRAME_TAG(0x47,> 3), and a Klein-4 turn/cap only ever holds bytes≤ 3, so headers are unambiguous; the label is recovered verbatim viatlv_unpack(a content-address cap is one-way — that is why a gene is tlv-framed, not capped). The leading telomere cap is skipped, sogenesneeds only the strand +the_one.- Additive only — no ABI change (ABI stays 3), numpy-free, native build holds no libm;
describe()["tools"]["total"]298 → 300 (tlv_unpack+genes). 5 SSOT bumped. Next in the arc:genome_export/import(.chr unit) thengenome_explode/pack+ catalog unify (register an exploded genome as an attested root; binary body kept).
[0.7.5rc133] - 2026-06-13¶
Mat / Vec become a near-total numpy-reflex SINK — the last 9 numpy idioms an LLM reflexively writes now route through srmech instead of bailing to numpy (research-env F728 carrier audit, 8/17 → 17/17). The carrier exists to stop an LLM dropping srmech and reaching for numpy: every numpy idiom the carrier ANSWERS routes through srmech silently; every idiom that RAISES bails the LLM to np.asarray(m.tolist()) → numpy, defeating the carrier. rc129/rc130 closed m[0] + @; rc133 closes the remaining 9 — and pins 17/17 so a regression that re-arms the numpy bail-out fails loudly.
- Elementwise / scalar arithmetic on both carriers:
__add__/__radd__/__sub__/__rsub__/__mul__/__rmul__/__truediv__/__rtruediv__/__neg__— soa + b,a - b,a * 2,2 * a,a / 2,2 / a,5 - a,-aall work, elementwise + scalar-broadcast, format-preserving (real ⊗ real → real, complex anywhere → complex).*is the elementwise (Hadamard) product exactly like numpy — matrix multiply stays@. Accepts a carrier OR a same-shape sequence; Class-K sign lives in the values, noabs(). - Slice-aware + negative-aware
__getitem__:Matnow answersm[:2](row slice →Mat),m[:, j]/m[a:b, j](a column →Vec),m[i, c:d](row's column-slice →Vec),m[a:b, c:d](sub-block →Mat), and negative tuple indicesm[-1, -1];Vecanswersv[:2](slice →Vec) andv[-1]. Format-preserving in every case (Mat→Matrow-slice/sub-block,Mat→Veccolumn,Vec→Vecslice). - Permanent ratchet: new
tests/test_carrier_reflex_sink_rc133.pymirrors the F728 audit — asserts all 17 idioms are ANSWERED (none raises), checks the arithmetic/slice VALUES are correct (not just non-raising), and re-runs numpy-hard-blocked. A regression that re-opens any idiom (re-arming the numpy bail) goes red. - Additive only — no ABI change (Python dunders; ABI stays 3), no public-name change,
describe()["tools"]["total"]stays 298, native build still holds no libm. numpy-free (verified numpy-absent); docstrings use·not a literal@(numpy-math ratchet unchanged). 5 SSOT bumped.
[0.7.5rc132] - 2026-06-13¶
Carrier-spirit lockdown — the tool schema stops advertising np.ndarray in the PARAMETER vocabulary too (the OTHER half of every tool signature; numpy was DELETED in #564). rc131 killed np.ndarray from all RETURN types and added the immolation gate; the parameter type strings still advertised np.ndarray ~150 times. Anywhere the API/introspection says np.ndarray is an INVITATION to an LLM to drop srmech and reach for numpy ("it's just a numpy array, do the math in numpy"). The carrier is the lock: advertise the srmech CARRIER (the numpy spirit without numpy), keep it AGNOSTIC about input (it accepts AND degrades to every form the no-math plan used — list / array.array / tuple / scalar / nested-list), and NEVER name numpy where a caller / LLM / introspection can see it.
- Parameter type-string sweep (
srmech.amsc.tool_schema): all 152np.ndarrayoccurrences inP(...)parameter type strings replaced with the carrier-spirit type by op semantics — by family: qm/laplacian matrix params (H/A/B/L/O/eta/rho/laplacian/M/V/g_v/g_s/g_c/A_segments) →Mat; 1-D vector / signal / state params (psi/state/v/k/k_spatial/direction/a/bdot-vectors/A_components) →Vec; HDC + genome hypervector params (klein4/polar byte vectors, octonion power-of-two vectors,the_one/turn/telomere/store) →HV; shape-polymorphic 2-D-or-1-D ops (elementwise_*,dense_norm,dense_solve.B,lstsq.b) →Mat | Vec; gaugegenerators/ einsumoperands→tuple[Mat, ...]; the rank-3 structure-constant tensor →list[list[list[float]]]; sequences →Sequence[HV]/Sequence[Vec];Optional[...]per semantics. The HARD RULE now holds on BOTH sides: noP(parameter type string NORreturns=R(type string intool_schema.pycontains the tokenndarray. - The carrier is AGNOSTIC about input (made real + tested): every numpy-free op already validates a 1-D / 2-D operand by iterating it (
_as_rows/_as_loop/_as_klein4_buf/Vec.from_sequence), so aMat-typed param accepts aMatOR list-of-lists OR tuple-of-rows; aVec-typed param accepts aVecOR flat list / tuple /array.array; anHV-typed param accepts anHVOR flat int list /array.array/ tuple.tests/test_immolation.pygains a focused test asserting a representative carrier-returning op gives the SAME result whether fed the carrier or its degenerate list / tuple / array form (dense_solve/dense_matvec_real/klein4_bind/loop_bind), plus a wire-path test that each carrier coercer produces an op-accepted structure — numpy-absent. - The MCP coercion lexicon + synth harness + JSON-schema lexicon extended:
srmech.mcp._coerciongainsVec/HV/Optional[Vec]/Optional[HV]/Mat | Vec/Sequence[Vec]/Sequence[HV]/tuple[Mat, ...]/list[list[list[float]]]coercers — each producing the natural Python structure (flat list forVec/HV, nested list forMat) the agnostic op consumes (NO numpy ever built); the legacynp.ndarraykeys are KEPT for the wire-form / round-trip tests.test_mcp.py's_synth_value_for_type+srmech.mcp._tools_TYPE_LEXICON/_ENCODING_HINTgain matching entries so the §10.1 every-tool harness synth-invokes every op cleanly and the advertised JSON schema never degrades a carrier param to a string. - The immolation param-guard (
tests/test_immolation.py): a new permanent assertion that NO advertised PARAMETER type (across all ToolEntries) containsndarray, mirroring the rc131 no-ndarray-in-returns guard — the lock is now on BOTH sides of every tool signature, and the numpy-hard-blocked slice asserts both halves numpy-absent. - Source prose scrub — stale
numpy.ndarray/ndarraymentions in the carrier accept-list docstrings (hv.pyHV.from_sequence,hdc.py_as_klein4_buf/_as_polar/_store_buf) reworded to the carrier-spirit ("the carrier is agnostic about the source shape"); the historical "(was an ndarray)" flip-notes are kept (they document numpy's removal, not an invitation). - Schema-vocabulary-only — no public op added or removed (
describe()["tools"]["total"]stays 298); ABI 3 (no C wire-format change); the native build still holds no libm. numpy-free (noimport numpy, nonp., verified with numpy hard-blocked); noabs()in cascade source; docstrings use·not a literal@token (the numpy-math ratchet is unchanged). All 5 SSOT version locations bumped.
[0.7.5rc131] - 2026-06-13¶
Introspection HONESTY + the immolation gate — the tool schema stops advertising np.ndarray returns for 88 ops it can no longer produce (numpy was DELETED in #564), and 8 straggler ops flip from bare lists to the Mat/Vec carriers. srmech's describe() / tool schema IS its Class-H self-recognition surface; advertising a returns type the package cannot make is a false self-description. Every advertised RETURN type is now the TRUE post-#564 carrier, determined by CALLING each op, and a named release gate (test_immolation.py) makes it permanent.
- Return-type honesty sweep (
srmech.amsc.tool_schema): all 88returns=R("…ndarray…")type strings corrected to the true carrier — by family: the qm matrix builders (spin/relativistic/gauge/sm/potentials/propagators/pseudo_hermitian/bell/single_particle/triality/so8) →Matortuple[Mat, …]; the klein4 HDC +genome.{quad_turn,telomere}ops →HV; the polar HDC ops →array; the loop-bind / octonion-vector /triality_applyops →list[float]; the structure-constants /octonion_mult_table→ genuine rank-3list[…];spectral.recompose/single_particle.tdse_evolve→list[complex]. The prose (2ndR()arg) is preserved verbatim; parameternp.ndarraytype strings are deliberately UNTOUCHED (a separate, synth-coupled concern). The HARD RULE now holds: noreturns=R(type string intool_schema.pycontains the tokenndarray. - 8 straggler carrier-flips (the carrier-format law
[[feedback_numpy_removal_must_preserve_carrier_format_mat_vec_not_lists]]):laplacian.{dense_solve,schur_complement,dirichlet_to_neumann}float path →Mat(2-D) /Vec(vector RHS), the exact path keepslist[list[Fraction]];cascade.matrix_cascades.{qr→tuple[Mat,Mat], svd→tuple[Mat,Vec,Mat] (singular values 1-D → Vec), lstsq→Vec|Mat, eigvals→Vec, einsum→Mat|Vec|scalar}— all routed through the existing numpy-freemat_*engine (no math reimplemented). A bare list has no honest C representation; aMat/VecIS a contiguous double buffer. - The immolation gate —
tests/test_immolation.py(the established sister-package release-gate name; ephemerides-spectral + antikythera-spectral each ship one): for EVERYmcp_callableToolEntry it synth-invokes and, when the op returns cleanly, assertstype(raw)AGREES with the advertisedreturns.type(carrier-aware union/tuple matcher inconftest) — the inspection the §10.1 every-tool smoke heldrawfor but never did; it asserts NO advertised return type namesndarray(a permanent down-only guard); and it folds in the rc130 carrier idiom-surface contract (calling real carrier-returning ops + asserting.shape/m[i,j]/m[0]→Vec/.T/.conj/@/…), then re-runs a representative slice with numpy hard-blocked at import.test_carrier_contract_rc130.pyis retired into it (no coverage lost).test_mcp.py's §10.1 gains the same return-type-agreement assertion (298/298 binding+coercion+return-type clean). - Introspection-honesty-only — no public op added or removed (
describe()["tools"]["total"]stays 298); ABI 3 (no C wire-format change); the native build still holds no libm. numpy-free (noimport numpy, nonp., verified with numpy hard-blocked); noabs()in cascade source; the numpy-math ledger lowerslinalg_fft3 → 2 (the rewrittenschur_complementdocstring dropped a stalenumpy.linalg.solvemention). All 5 SSOT version locations bumped.
[0.7.5rc130] - 2026-06-13¶
The Mat / Vec carriers gain the last two numpy idioms — single-index row m[0] (→ Vec) and the @ matmul operator — and a carrier-contract smoke that would have CAUGHT both gaps (#564 follow-up). rc129 restored the carrier FORMAT but left two numpy idioms unimplemented: m[0] raised "Mat index must be (i, j)" (forcing m.row(0)) and @ was unsupported (forcing laplacian.dense_matmul_*). These were the holes a downstream numpy-idiom compat sweep surfaced. rc130 closes them and — answering "how did the smoke miss this?" — adds the behavioural net the registry-walk smoke structurally could not be.
Mat.__getitem__is now rank-polymorphic:m[i, j]→ a plainfloat/complexscalar (unchanged);m[i](a single int) → rowias aVec(the numpy single-index idiom — format-preserving, NOT a bare list; negative indices supported). The explicit stdlib-list accessorm.row(i)is unchanged.@matmul on both carriers, routed onto the Class-L cascade (never numpy):Mat.__matmul__/__rmatmul__—Mat·Mat→Mat(dense_matmul_*),Mat·Vec(or a flat 1-D sequence) →Vec(dense_matvec_*);Vec.__matmul__/__rmatmul__—Vec·Vec(or a flat sequence) → a scalar inner product (dense_dot_*),Vec·Mat(row-vector · matrix) →Vec. Format-preserving: real ⊗ real → real carrier, complex anywhere → complex carrier. Two shared numpy-free detectors (_is_matrix_like/_carrier_is_complex) pick matmul-vs-matvec and the real-vs-complex Class-L peer.- Smoke completeness — the answer to "the smoke missed these": the registry-walk smoke (
test_registry_smoke_rc127) only checks that each registered tool NAME resolves to a callable and each catalog class describes — a name-resolution + introspection net that never calls an op nor touches a returned carrier, so a carrier-method gap is invisible to it. Newtests/test_carrier_contract_rc130.pyis the behavioural net: it CALLS representative carrier-returning ops (dense_laplacian/dense_adjacency/jacobi_eigvals/fiedler_vector/dense_matmul_real/dense_matvec_real/dense_outer_real) and asserts theirMat/Vecreturns expose the full documented numpy-idiom surface (.shape,len, iteration,m[i, j],m[0],.T/.transpose(),.conj(),.tolist(),.tobytes(), value-==,@) — and re-runs it numpy hard-blocked at import. A future op that returns a bare list (the rc127 regression) or a carrier that drops an idiom now fails loudly here. - Carrier-idiom-only — no new public op (
describe()["tools"]["total"]stays 298); ABI 3 (no C wire-format change); the native build still holds no libm. numpy-free (noimport numpy, nonp., verified with numpy hard-blocked); noabs()in cascade source; the numpy-math ledger is unchanged (the new docstrings use·, not a literal@token). All 5 SSOT version locations bumped.
[0.7.5rc129] - 2026-06-12¶
Carrier FORMAT restored — the Class-L dense_* / eigendecompose / graph-build / elementwise ops return Mat (matrices) / the new Vec (vectors), NOT bare Python lists (#564 correction). rc127's numpy-removal regressed ~17 ops in srmech.amsc.{laplacian,coupling,harmonics} from the numpy-carrier to bare Python lists/tuples — losing .shape, the 2-D/1-D structure, and any honest C representation (a list is not something in C; a contiguous double / interleaved double _Complex buffer is). rc129 restores the numpy-carrier FORMAT numpy-free: the math stays the exact cascades, only the return carrier is fixed. This supersedes the rc127 "now return plain Python lists" BREAKING note — the carriers re-expose .shape + a native C interleaved-buffer wire form, so the downstream .shape / 2-D-structure / 1-D-vector consumers (e.g. ephemerides-spectral) are fixed.
- New
Veccarrier (srmech/amsc/vec.py) — the numpy-free 1-D peer ofMat: a flatarray('d')(real →ndoubles; complex →2ninterleaved(re, im)doubles, C99double _Complexorder),is_complexflag. Surface mirrorsMat:.shape -> (n,)(1-tuple, like a 1-D ndarray),__len__,__getitem__(i) -> float|complex(plain scalar),__iter__yields SCALARS (not rows),__eq__→ scalar bool (vsVec/sequence; never imports numpy),.conj()(Class-K imag sign-flip),from_sequence/from_flatclassmethods,tolist/tobytes/.buffer,__repr__. The C wire helper islaplacian._vec_to_interleaved_cbuf(+_vec_from_interleaved_cbuf) — the 1-D twin of theMatmarshallers, so aVecisctypes-castable to the native dense buffer exactly like aMat(zero-copy for complex,(re, 0)-filled once for real). - Flipped →
Mat(2-D matrix returns):dense_adjacency/dense_laplacian/normalized_laplacian/signed_laplacian/magnetic_laplacian,dense_matmul_complex/dense_matmul_real,dense_outer_complex/dense_outer_real, the eigenVECTORS ofhermitian_eigendecompose/symmetric_eigendecompose, and the per-band eigenvector groups ofthree_fold_eigvec_groups(the returned dict'slow/mid/highare realMats). - Flipped →
Vec(1-D vector returns):fiedler_vector,dense_matvec_complex/dense_matvec_real,jacobi_eigvals(eigenvalues), the eigenVALUES ofhermitian_eigendecompose/symmetric_eigendecompose,spectral_block_dispatch(per-block + combined spectra), andcoupling.signed_sum_squared(the small non-negative integer scores are exact as float64 doubles — well within the 2⁵³ exact-integer range; noted in the docstring). - Shape-polymorphic (preserve input rank):
elementwise_multiply_complex/elementwise_transcendental/elementwise_hypot/elementwise_sqrt— aMat/2-D input →Matout, aVec/1-D-sequence input →Vecout (rank read off the input). Unchanged scalars:dense_dot_real -> float/dense_dot_complex -> complex(already scalars, not carriers). - Internal chaining + MCP boundary:
jacobi_eigvals(dense_laplacian(...))and thesymmetric_eigendecompose→three_fold/fiedlerchains accept the carrier natively (m[i, j]/v[i]); the MCPserialise_nativegains aVecbranch (flat JSON list; complex →[re, im]leaves) next to the existingMatbranch.harmonics.classify_chirality_harmonicacceptsVec/Mat/Sequence(read element-by-element). - Carrier-FORMAT-only — no new public op (
describe()["tools"]["total"]stays 298); ABI 3 (no C wire-format change; theVecmarshaller is Python ctypes only); the native build still holds no libm. numpy-free verified with numpy hard-blocked atsys.meta_path(carrier ops import + run numpy-absent); noabs()in cascade source (the Class-K sign-branch is inMat/Vec.conj+ the signed-degree magnitude). Newtests/test_vec_carrier_rc129.py; ~16 carrier-consuming test files updated to assert via theMat/VecAPI (m[i, j]/v[i]/.tolist()/.shape). All 5 SSOT version locations bumped.
[0.7.5rc128] - 2026-06-12¶
§41 genome persistence — the Genome catalog class learns to save/load itself to disk, attested and numpy-free (PR #687 UPSTREAM §41; #564 C/Python-1:1 arc). A genome strand (telomere-capped, Klein-4 quad-turn-coupled leaves) now round-trips to an on-disk directory as a provenance-attested artefact — the FIRST half of the §41 1:1 pair (the C srmech_json + srmech_genome mirror follows in this branch).
- Five new public ops in
srmech.amsc.genome(describe()["tools"]["total"]293 → 298):genome_save(strand, path, *, the_one, labels)/genome_load(path, *, labels=None)/genome_catalog(path)/genome_append(path, label, leaves, *, the_one)/genome_window(path, label), plus a newGenomeBoundingError. - On-disk format —
path/is a directory:manifest.jsonis anMPRRecord(attestation.response_sha256 == body_sha256,parser_version= the srmech version) whosedatacarries{format_version, leaf_dim, n_turns, the_one:{sha256,hex}, body_sha256, chromosomes:[{label, cap_sha256, leaf_count, byte_offset, byte_len}]};turns.binis an append-only flat concatenation of fixed-widthleaf_dim-byte Klein-4 (0..3) leaf blocks (turn k = bytes[k·leaf_dim : (k+1)·leaf_dim], no length prefixes). - Disk-paging + bounding-as-integrity:
genome_load(path)streams block-by-block;genome_window/ labelledgenome_loadseek tobyte_offsetand read onlybyte_len(RAM bounded by the largest single chromosome). Every read re-hashes viaformat.sha256_bytesagainstbody_sha256(whole-genome) or the region cap vscap_sha256(windowed) →GenomeBoundingErroron mismatch.genome_catalogreads the manifest ONLY (never opensturns.bin).genome_appendis append-only — every prior chromosome'scap_sha256/byte_offset/leaf_countstays byte-identical; onlyn_turns/body_sha256/ the new entry change. - Catalog-class surface: the shipped
Genome[class]descriptor gainssave/load/catalog/appendmethods (mirroring the existingassemble/partitionbindings). - numpy-free (no
import numpy, nonp., verified passing with numpy hard-blocked at import); hashing routed throughformat.sha256_bytes; noabs()in cascade source. Registry checklist satisfied (5ToolEntryregistrations + 5rosetta_classification.ndjsonnon_computelines + thedescribecount-test bump). ABI 3; the native build still holds no libm. - C mirror —
srmech_json(malloc-free JSON parser + canonical writer;c/src/srmech_json.c): the keystone the genome-persistence C mirror needs. The parser builds a value tree (srmech_json_value_t) from a caller-supplied arena/workspace (the samevoid *ws, size_t ws_lenbump allocator the TOML parser uses); the writer emits bytes BYTE-IDENTICAL to CPythonjson.dumps(obj, sort_keys=True, ensure_ascii=False)for any tree of null / bool / int / string / object / array — exactly what an MPR manifest / genome catalog is. Object keys sorted by UTF-8 byte order (= code-point order = Pythonsorted());": "/", "separators; strings escape only" \ \b \t \n \f \r+ other controls< 0x20as\u00XX(lowercase), withensure_ascii=Falseso all bytes ≥ 0x20 (incl. multibyte UTF-8) emit verbatim. New symbols:srmech_json_parse/srmech_json_write(NULL buf ⇒ size query; too-small buf ⇒SRMECH_ERR_OVERFLOW, never overflows) /srmech_json_object_get+ a builder (srmech_json_builder_init/srmech_json_new_{null,bool,int,double,string,array,object}). Float caveat:SRMECH_JSON_DOUBLEvalues are written best-effort (%.17g); byte-parity with Python'srepr(float)is NOT guaranteed — the parity GUARANTEE covers null/bool/int/string/object/array trees only (manifests are float-free). JPL-clean (no recursion — explicit depth-bounded stack capped atSRMECH_JSON_MAX_DEPTH; no malloc — caller arena; ≤60-line functions; ≥2 asserts/non-exempt-fn;json_is_ws/json_is_digitare the 2 Rule-5-exempt char classifiers). ABI stays 3 (additive symbols + a struct + macros). - C mirror —
srmech_genome(the §41 persistence mirror;c/src/srmech_genome.c) — completes the §41 1:1 pair.srmech_genome_savewrites<dir>/turns.bin(= the body verbatim) +<dir>/manifest.jsonbuilt via thesrmech_jsonbuilder and serialized BYTE-IDENTICAL to Pythongenome_save's manifest; all hashing routes throughsrmech_sha256_hex(the§incite_asemitted as raw UTF-80xC2 0xA7;parser_version="srmech " SRMECH_VERSION;parser_rule_hash/collector_descriptor_hashthe sha256 of the same ASCII inputs as Python).srmech_genome_catalogparses the manifest only (never opensturns.bin);srmech_genome_load/srmech_genome_windowre-hash the bytes read againstbody_sha256/cap_sha256and return a bounding error on mismatch;srmech_genome_appendgrowsturns.bin+ rewrites the manifest leaving prior chromosome entries byte-identical. New symbols:srmech_genome_save/_catalog/_load/_window/_append+srmech_genome_chrom_t. JPL-clean (file I/O via stdio, json tree in the caller arena; no malloc/libm/goto/recursion; ≤60-line fns; ≥2 asserts; no new Rule-5 exemptions). ABI stays 3. Verified standalone in WSL2 (no Python): pedantic-Werrorbuild clean; C smoke 18/18; the C↔Python byte-parity harness showsmanifest.json(1747 bytes) ANDturns.bin(576 bytes) reproduced byte-for-byte over the real synthetic genome, plus the catalog/load/bounding round-trip.
[0.7.5rc127] - 2026-06-12¶
numpy is GONE — the whole package AND the whole test suite run with numpy NOT installed (CEIL_NUMPY_CARRIER 1 → 0, the capstone of #564). This is the big-bang completion of the carrier-removal arc: not numpy-optional, not behind a [scientific] extra, not a lazy proxy, not a .to_numpy() export — numpy is removed, period. pip install srmech pulls no numpy; a fresh numpy-absent venv imports + runs the entire API and the entire pytest suite green.
- The last top-level carrier flips numpy-free:
mcp/_coercion.py(MCP arg coercion) routes toMat/ plain lists —_to_ndarray→ nested list,_complex_pairs_to_ndarray→ recursive[re, im]→complexlist,serialise_nativegains aMatbranch and drops the ndarray/np.genericbranches.CEIL_NUMPY_CARRIERreaches 0 — the carrier ratchet is now the permanent "ZEROimport numpyanywhere insrmech/" guard. - The
[scientific]hint machinery is DELETED:srmech/_scientific.py(require_numpy/_LazyNumpy/lazy_numpy/make_lazy_op_getattr) is gone; its importers (qm/__init__,signal_processing/closed_form_ops/__init__,signal_processing/path_b_ops/__init__) de-wire to plain eager re-exports /__getattr__. - The export boundary is DELETED:
One.to_numpy()/One.to_matrix()(→Mat),HV.to_numpy(),Mat.to_numpy()— the lossy numpy-export convenience methods are removed (they were the last "numpy-optional" survivors; #564 retires the "lossy export convenience, kept" stance entirely). - Remaining numpy MATH flips onto the
mat_*engine + plain lists (laplacian.pydense_/eigendecompose/graph-build/graph-spectral,matrix_cascadesqr/svd/lstsq/einsum/eigvals,coupling,harmonics,atoms,_fft_carrier). *BREAKING API change:** thedense_*/*_eigendecompose/ graph-build / graph-spectral ops now return plain Python lists (matrices = list-of-lists, vectors / eigenvalues = flat list, scalars =float/complex) instead ofnp.ndarray;schur_complementfloat →list[list[float]], exact →list[list[Fraction]]. Downstream consumers that relied on ndarray returns (e.g. ephemerides-spectral) need a numpy-free-consumption follow-up. - Packaging + CI: both pyprojects drop the
scientific = ["numpy"]extra and numpy fromtests/dev; the description is "numpy-FREE"; the srmech-ci pure-wheel guard is the numpy-ZERO capstone (a hardfind_spec('numpy') is Noneassert) and the staleOne.to_numpy()must-raise is gone. - Tests: ~72 numpy-oracle / ndarray-dispatch test files rewritten numpy-free (oracles → stdlib
cmath/fractions/struct/math/random.Random, the srmech exact path, or reconstruction/residual invariants); the obsolete[scientific]-machinery / must-raise-hint test functions are deleted;test_numpy_math_ratchetceilings recomputed. This rc closes the last two in-functionimport numpytest gaps (test_introspectdescribe_shape →Mat;test_cascade_sedenion_registerun-skips its now-numpy-free storage + working-word tests). A new registry-walk smoke (test_registry_smoke_rc127.py+ the pure-wheel CI step) walks the FULL tool registry (all 293tool_schemaentries resolve to a real attribute) and the FULL class registry (every shipped catalog class —Genome,Hurwitz— describes), numpy-absent — so a newly-registered tool whose module fails to import, or a shipped class that fails to describe, fails immediately instead of via a hand-picked subset. Full suite verified numpy-genuinely-absent: 3506 passed, 2 skipped — the only failures are the 3 stale-local-DLL version-agreement tests (they pass on CI's fresh build). - No new public op (
describe()["tools"]["total"]stays 293); ABI 3; the native build still holds no libm. All 5 SSOT version locations bumped.
[0.7.5rc126] - 2026-06-12¶
signal_processing/closed_form_ops/ica_jade.py (JADE blind-source-separation / independent-component analysis) goes numpy-FREE (CEIL_NUMPY_CARRIER 2 → 1) — the LAST srmech.signal_processing carrier, so the whole srmech.signal_processing subpackage is now numpy-free. Carrier-removal #564. Per the no-bridge discipline (grep -E "\bnp\.|import numpy|_lazy_numpy" = 0): drops import numpy as np and the dense_matmul_real / hermitian_eigendecompose / elementwise_sqrt numpy-carrier imports.
- Whitening eig onto the Mat carrier: the covariance eigendecomposition routes through
mat_hermitian_eigendecompose(the priorhermitian_eigendecompose/dense_matmul_realare numpy carriers that raise numpy-absent — the rc70 runnable≠loadable trap). The covariance build, PCA whitening, whitened-source projection, and the final unmixing are explicit numpy-free loops over plainfloatlists; the inverse-√λwhitening scales use the Class-Nrational.sqrt. - Cumulants + Givens: the fourth-order cumulant tensor is nested Python lists (
Matis 2-D only); the Givens-rotation joint diagonalisation is an explicit numpy-free sweep — the rotation angle is the Class-Nrational.atan2andc,s = rational.cos/sin, the off-diagonal magnitudes are Class-K sign-branches (noabs()), and the two cumulant-axis rotations (originallynp.einsum("ai,ijlm->ajlm", G.T, …)) become explicit first-axis contractions — preserving the original behaviour byte-for-byte incl. the inertif Falsealternate-subscript branch (carrier-swap, not a bugfix).opreturns the sourcesSand unmixing matrixWasMat(was an array return). - CI-GUARD STRUCTURAL SHIFT:
ica_jadewas the pure-wheel must-raise[scientific]exemplar; now numpy-free, the exemplar MOVED to the durable export boundaryOne.to_numpy()— a lossy ndarray export that intrinsically needs numpy and is NOT a carrier to be flipped, so it stays the long-lived numpy-requiring exemplar past the carrier-removal capstone. Thetest_signal_processing_numpy_free_reachable_rc71clean-hint test repointed to the same surface;ica_jadeadded to the flipped-and-exercised list.test_ica_jade_smokerewritten numpy-FREE (random.Randomsources, list mixing); new_BLOCK_NUMPYreachability proof (incl. a full-srmech.signal_processing-imports-numpy-free assertion). - Carrier-only — no new public op (
describe()["tools"]["total"]stays 293); ABI 3; no C change. The matmul math-ledger drops 4 → 2 (the 2 cumulant-rotationnp.einsumcalls gone; thea·bmatmul docstring de-@-ed);linalg_fft/ufuncunchanged. All 5 SSOT version locations bumped. Remaining 1 carrier:mcp/_coercion— the rc127 CAPSTONE (CEIL_NUMPY_CARRIER1 → 0 + delete the must-raise clause + numpy-absent fresh-import asserts).
[0.7.5rc125] - 2026-06-12¶
srmech.spectral (Spike #115 runtime spectral encode/decode surface) goes numpy-FREE (CEIL_NUMPY_CARRIER 3 → 2). Carrier-removal #564. A LEAF for its public ops — the only srmech-side importer is mcp/_coercion.py, which imports the SpectralHandle type (not any function); nothing in srmech calls decompose/recompose/predict/truncate_sparse. Per the no-bridge discipline (grep -E "\bnp\.|import numpy|_lazy_numpy" = 0): drops import numpy as np and the dense_matvec_complex / hermitian_eigendecompose / elementwise_hypot / elementwise_transcendental numpy-carrier imports.
- Math onto the Mat carrier: the Hermitian eigendecomposition routes through
mat_hermitian_eigendecompose(the priorhermitian_eigendecompose/dense_matvec_complexare numpy carriers that raise numpy-absent — the rc70 runnable≠loadable trap); the projectionVᴴ·state(decompose) and reconstructionV·coeffs(recompose) are explicit numpy-free matvecs over the eigenvectorMat. The eigenbasis LRU cache now stores(eigvals_list, V_Mat). - Bytes / sort / phase / magnitude: the complex128 coefficient bytes (
np.frombuffer/.tobytes) becomestructpack/unpack of interleaved native-endian(re, im)float64 pairs — byte-identical to the prior layout, so the substrate-descriptor and content-SHA hashes are stable (verified).np.argsort(-mag, stable)→sorted(range(n), key=…, reverse=True)(stable, same tie order); the per-modepredictphasee^{-iλt}→ the Class-N Euler cascaderational.cexp; thetruncate_sparsemagnitudes → squared-modulusre²+im²(monotone, noabs()/sqrt — used for both the top-k rank and the threshold gate viathr²). recomposenow returnsList[complex](wasnp.ndarray); the MCP dispatch path serialises via the JSON wire (.tolist()-style), so the return-type change is transparent to callers.test_spectral.py+test_spectral_rcn_plus_2.pyrewritten numpy-FREE (structunpack,max(abs(…)),random.Randomfixtures, list-of-lists Laplacians); new_BLOCK_NUMPYreachability proof.- CI-GUARD:
ica_jadestays the pure-wheel must-raise[scientific]example (spectral ≠ ica_jade) — no guard move. Carrier-only — no new public op (describe()["tools"]["total"]stays 293); ABI 3; no C change; math-ledger unchanged (spectral's numpy was bytes/sort coercions, not linalg/fft/matmul/ufunc). All 5 SSOT version locations bumped. Remaining 2 carriers:mcp/_coercion(the capstone) /signal_processing/.../ica_jade.
[0.7.5rc124] - 2026-06-12¶
qm/pseudo_hermitian.py (η-pseudo-Hermitian / PT-symmetric framework) goes numpy-FREE (CEIL_NUMPY_CARRIER 4 → 3) — the LAST srmech.qm carrier, so the whole srmech.qm subpackage is now numpy-free. Carrier-removal #564, a LEAF qm module (its only consumers are tool_schema metadata + the qm/__init__ re-export). Per the no-bridge discipline (grep -E "\bnp\.|import numpy|_lazy_numpy" = 0): drops import numpy as np, the dense_* numpy-carrier imports (_dense_solve_complex/dense_dot_complex/dense_matmul_complex/dense_matvec_complex/dense_norm), and matrix_cascades as _mc.
- Matrices ride the framework-native
Mat; state vectors are plaincomplexlists (Matis 2-D only).inner_product_eta/expectation_etaare explicit Class-L matvec + sesquilinear-dot cascades;is_pseudo_hermitianis‖O†η − ηO‖viamat_matmul+mat_norm. The spectrum comes from the Mat-carriermat_eigvals(the lonenp.linalg.eigis gone —linalg_fftmath-ledger 9 → 8). - The η = (V·V†)⁻¹ construction needs the general non-Hermitian eigenvectors of a real-spectrum O: each is the null vector of
O − λIcomputed by deterministic Gaussian elimination (squared-modulus pivots — a Class-K magnitude, noabs()), avoiding any ill-conditioned shifted-inverse solve. η is the inverse of the HPD Gram V·V† via the well-conditionedmat_solve(no rank-deficiency → no native-vs-pure-Python singular-solve divergence). The identity O†η = ηO holds for any column scaling of V, so the eigenvectors need no normalisation. test_qm_pseudo_hermitian.pyrewritten numpy-FREE (fixed smallMatfixtures; the Hermitian-eigenstate oracle ismat_hermitian_eigendecompose, not numpy); new_BLOCK_NUMPYreachability proof (incl. a full-srmech.qm-imports-numpy-free assertion). The rc60/rc63/rc66 routing tests updated: rc63_ROUTEDnow empty (pseudo_hermitian graduated, like so8/esprit); rc66 asserts themat_solveroute (was_dense_solve_complex).- CI-GUARD: the pure-wheel must-raise
[scientific]example MOVEDpseudo_hermitian→ica_jade(from srmech.signal_processing.closed_form_ops import ica_jade, a still-numpy carrier whose import re-raises the clean hint via theclosed_form_opslazy package gate);pseudo_hermitianadded to the flipped-and-exercised list. Carrier-only — no new public op (describe()["tools"]["total"]stays 293); ABI 3; no C change. All 5 SSOT version locations bumped. Remaining 3 carriers:mcp/_coercion/signal_processing/.../ica_jade/spectral/__init__.
[0.7.5rc123] - 2026-06-12¶
qm/propagators.py (Feynman scalar / fermion / photon / massive-vector propagators) goes numpy-FREE (CEIL_NUMPY_CARRIER 5 → 4). Carrier-removal #564 — a LEAF qm module (its only consumers are tool_schema metadata + the qm/__init__ re-export, so no connected-component cascade). Per the no-bridge discipline (grep -E "\bnp\.|import numpy|_lazy_numpy" = 0): drops import numpy as np and the dense_matvec_real/dense_outer_real numpy-carrier imports; the four propagators are built over the framework-native Mat + Python complex.
- The fermion-propagator numerator
i(p̸+m)consumesrelativistic.dirac_operator_momentum_space(...)(aMat, numpy-free since rc118) DIRECTLY — the three rc118.to_numpy()bridges propagators carried (the fermion numerator + the twominkowski_metric()coercions for the photon / massive-vector) are DELETED (consume theMatdirectly). Thekᵘkᵛtensor routes through a numpy-free_outer(u,v)(column·rowmat_matmul); thek²contraction reuses relativistic's numpy-freefour_momentum_squaredfold. Returns: matrices →Mat, scalar propagators →complex(same physics, identical signatures). Nomat_solve(pure matmul/outer), so no native-vs-pure-Python divergence risk. test_qm_propagators.pyrewritten numpy-FREE (pole structure, Clifford(p̸+m)(p̸−m)=p²−m², photon transversality viaMat-entry/mat_matmul/complex); new_BLOCK_NUMPYreachability proof; thetest_dense_outer_rc51propagators cell updated. CI-GUARD: the pure-wheel must-raise[scientific]example MOVEDpropagators→pseudo_hermitian(still a numpy carrier);propagatorsadded to the flipped-and-exercised list. Carrier-only — no math-ledger change; no new public op (describe()["tools"]["total"]stays 293); ABI 3; no C change. All 5 SSOT version locations bumped. Remaining 4 carriers:qm/pseudo_hermitian/mcp/_coercion/signal_processing/.../ica_jade/spectral/__init__.
[0.7.5rc122] - 2026-06-12¶
The octonion connected component goes numpy-FREE in one coordinated cluster flip (CEIL_NUMPY_CARRIER 8 -> 5). Carrier-removal #564, done as a single honest unit per the discipline that numpy-free means ZERO numpy -- no import numpy, no np. callsite, no latent _lazy_numpy proxy, and no .to_numpy()/np.asarray bridge anywhere. The gate is literal: grep -E "\bnp\.|import numpy|_lazy_numpy" returns zero for every file touched. Because the no-bridge rule forbids "wrap the still-numpy consumer," octonion could not flip alone -- the whole dependency component (hdc octonion-loop path -> qm/octonion -> qm/so8 + qm/triality + amsc/cascade/hypercomplex_dft) is removed together, decrementing three carriers at once.
amsc/hdc.py-- thenp = _lazy_numpy(...)proxy is deleted; the octonion loop family (loop_bind/conj/inv/associator/cross7/g2_three_form/left_op/right_op+ the*_hdwhole-array peers + the polar/klein4 HDC family) marshals to/from the native C dispatch with no numpy (ctypes(c_double*n)buffers +list[float]/Matreturns; the two matrix ops return a realMat); numpy-free pure-Python fallbacks operate on lists. Native dispatch is preserved (no Rosetta-ledger regression); numpy-present results are bit-for-bit identical (||tau^3 - I|| = 3.22e-15, unchanged).qm/octonion.py-- the (8,8,8) structure-constant tensor is a nestedlist[list[list[int]]];octonion_mult_table()returns it,octonion_left_mult/octonion_right_multreturn a real 8x8Mat,octonion_conjugatealist[float],octonion_normafloat(Class-Kmagnitude->rational.sqrt, noabs()). The Class-A self-attestationresponse_sha256is byte-identical to the oldnp.int8.tobytes()path (7f36461e...ab5b, unchanged).qm/so8.py-- full rewrite ontoMat+ themat_*family. The dual numpy use is resolved exactly: matrix-RANK on exact {-1,0,+1} data -> an exact RREF over the rationals (fractions.Fraction, mathematically identical and numpy-free); nullspace geometry -> the numpy-freemat_svdfloat carrier (smallest-singular-value vectors by known nullity, never a tolerance -- honouring the cascade-SVD small-singular-value accuracy floor). Fixes a genuine latent defect surfaced by the flip: the eigenspace Rayleigh quotient used a vdot-style bilinearv.v(~0 for a complex eigenvector of a real-skew matrix); now the explicit Hermitiansum(conj(v_i)*v_i).qm/triality.py--Mat/mat_matmul/mat_norm; the order-3 companion solve moved off the over-256-boundmat_lstsq(pure-Python 512-wide, ~309 s) onto sparse normal equations(A^T A)x = A^T rhsvia nativemat_solve(~1.2 s), value-identical.amsc/cascade/hypercomplex_dft.py-- drops the function-local numpy; consumes theMatoctonion operators via a numpy-free_matvec8.qm/hurwitz.py(consumer, already 0-numpy) -- nested-list indexingtable[a][b][axis]for the now-list octonion table.
Every .to_numpy() bridge prior rcs had parked in so8/triality is deleted (rewritten to .tolist()). The 14 cluster + loop-family test files are rewritten numpy-FREE (so8's rank tests exercise the rank-deficient regime -- rank_with_so4 stays 6); a _BLOCK_NUMPY subprocess test proves all five modules import+run numpy-genuinely-absent. Also a small test-harness robustness fix: test_dsl.py::_run_cli now decodes the CLI subprocess as UTF-8 (encoding="utf-8") so it is locale-robust (a cp1252-default box previously raised UnicodeDecodeError on the unicode op descriptions). CI-GUARD: octonion/so8/triality/hdc/hypercomplex added to the pure-wheel flipped-and-exercised list; must-raise example stays propagators. No new public op (describe()["tools"]["total"] stays 293); ABI 3; no C change. All 5 SSOT version locations bumped. Remaining 5 carriers: qm/propagators / qm/pseudo_hermitian / mcp/_coercion / signal_processing/.../ica_jade / spectral/__init__.
[0.7.5rc121] - 2026-06-12¶
qm/potentials.py (hydrogen radial + harmonic oscillator) goes numpy-FREE (CEIL_NUMPY_CARRIER 9 → 8). Carrier-removal #564 — the breach the rc120 native-eig-bound raise was opened for. (rc120 was a foundation rc that did not decrement the ratchet, so it stayed at 9; potentials is the 9→8 flip.) hydrogen_radial's real-symmetric tridiagonal Hamiltonian (n_grid up to ~600) now eigensolves on the fast native C Jacobi via mat_hermitian_eigendecompose (rc120 lifted the native bound to n≤2048), with no compromise — no API-default change, no slow pure-Python fallback.
srmech/qm/potentials.pydropsimport numpy as np(anddense_matmul_complex/hermitian_eigendecompose).hydrogen_radialbuilds the tridiagonal H as aMatand returns(r: list[float], energies: list[float], eigenvectors: real Mat)— the eigenvalues are the ascending(n,1)real Mat as a list, the eigenvectors the value-preserving real part (H is real-symmetric).harmonic_oscillator_ladderreturns the lowering/raising operators as complexMat(√n via the Class-Nrational.sqrt;a† = a.conj().T), andharmonic_oscillator_hamiltonianisω(a†a + ½I)viamat_matmul. Verified numpy-genuinely-absent.test_qm_potentials.pyrewritten numpy-FREE (commutator / spectrum viamat_matmul+mat_hermitian_eigendecompose, ladder action by directMat-entry, hydrogen via list indexing + real-Matmat_matmul, the harmonic-oscillator↔TDSE cross-check passes theMatH directly tosingle_particle.tdse_evolve). Test grids tuned to n_grid=250 (≈3 s/eig on the native path) landing inside the unchanged accuracy bands (ground −0.496 ∈ (−0.51,−0.48), 2s −0.125, 2p −0.125). The potentials cell intest_qm_cascade_routing_rc33.pyis numpy-free too (numpy stays in that file only as the differential oracle for the still-unflipped generichermitian_eigendecompose).
CI-GUARD: potentials added to the pure-wheel flipped-and-exercised list (must-raise example stays propagators). Carrier-only — no math-ledger change; no new public op (describe()["tools"]["total"] stays 293); ABI 3; no C change (rides the rc120 native-bound foundation). All 5 SSOT version locations bumped. Remaining 8 carriers: so8 / octonion / triality / propagators / pseudo_hermitian + mcp/_coercion + spectral/__init__ + ica_jade.
[0.7.5rc120] - 2026-06-12¶
Native Hermitian eigendecomposition now serves n up to 2048 (was 256) — foundation for large-grid numpy-free eig (#564). The native C srmech_hermitian_eigendecompose path was capped at n ≤ 256; above that, mat_hermitian_eigendecompose fell to the pure-Python cyclic Jacobi, which is impractically slow for the matrix sizes real QM operators need (e.g. qm.potentials.hydrogen_radial defaults to n_grid=400 and tests use 600). This rc lifts that cap with no compromise: it routes through the reentrant srmech_hermitian_eigendecompose_ws C entry (shipped since #772), which takes a caller-supplied workspace — Python allocates it via ctypes (heap, not a C malloc → JPL Rule 3 clean; not the stack → no overflow), so n is bounded only by a sanity constant, not by storage.
- C: added
SRMECH_HERMITIAN_WS_MAX_NODES = 2048(srmech.h) and pointed the five hermitian-eig-path bound checks insrmech_laplacian.c(off_diag_sq/init_identity/run_sweeps/_wsassert + hard cap) at it.SRMECH_LAPLACIAN_MAX_NODESstays 256 — the graph-laplacian-build stack arrays (degree[256]/d_inv_sqrt[256]) and the non-_wsconvenience wrapper's 1 MB static buffer are untouched (the non-_wswrapper stays self-limited to 256 via itsws_len < 2·n·ncheck). The whole hermitian-eig path works in-place on the caller's workspace + eigenvector output — there are no O(n²) stack arrays. ABI stays 3 (additive symbol + internal bound relax; no wire-format change). Pedantic/W4 /WXbuild clean; JPL ratchet unchanged. - Python:
_native.pybinds the (guarded)srmech_hermitian_eigendecompose_wssymbol;mat_hermitian_eigendecomposeprefers it with a(2·n·n)-doublectypesworkspace whenn ≤ MAX_NATIVE_HERMITIAN_NODES(new constant = 2048), falling back to the non-_wsentry forn ≤ MAX_NATIVE_NODES(256, unchanged) on older libs, then to the pure-Python Jacobi on a convergence miss.MAX_NATIVE_NODES(256) still gates the genuinely-256-bounded paths (dense_solvestatic buffer,dense_matvec/matmul,jacobi_eigvals).
Verified: 400×400 native Hermitian eig in 16.5 s (vs ~29 min on the pure-Python fallback — 140× at n=128), eigenvalues correct to ~2e-12; n=200 + n=2 paths unchanged. Foundation rc — no carrier flip (CEIL_NUMPY_CARRIER stays 9), no new public op (describe()["tools"]["total"] stays 293); this unblocks qm/potentials.py flipping numpy-free with the fast native path intact at n=400/600 (next rc). All 5 SSOT version locations bumped.
[0.7.5rc119] - 2026-06-12¶
qm/gauge.py (Yang-Mills SU(2)/SU(3)) goes numpy-FREE (CEIL_NUMPY_CARRIER 10 → 9). Carrier-removal #564 — the gauge-theory layer (SU(2)/SU(3) generators / eight Gell-Mann matrices / structure constants / Casimirs / connection / path-segment holonomy / Wilson loop). The generator matrices are held in the framework-native Mat; every matrix product routes through the Class-L mat_matmul, residual norms through mat_norm, and the path-segment holonomy exp(i g A^a T^a) through mat_hermitian_eigendecompose (V·diag(e^{iλ})·Vᴴ, the e^{+iλ} phase the Class-N rational.cexp Euler cascade) — no numpy.
srmech/qm/gauge.pydropsimport numpy as np(and thedense_matmul_complex/dense_norm/elementwise_transcendental/hermitian_eigendecomposecarrier imports). The SU(2) generators consume the rc115 numpy-freespin.pauli_matrices()2×2Matblocks directly — so the rc115.to_numpy()boundary coercion that this module carried is removed (gauge was the last consumer of spin's Pauli producer). The eight Gell-Mann matrices are exact small-integerMat(λ⁸'s1/√3via the Class-Nrational.sqrt);casimir_operator/gauge_connection_matrixare explicitMatfolds;casimir_eigenvalueistrace(C₂)/dim;lie_algebra_residualreadsf[a][b][c]. The structure constantsf^{abc}become plain nested Python lists (rank-3;Matis 2-D only). Verified runs numpy-genuinely-absent: the SU(2)/SU(3) Casimir eigenvalues are exactly ¾ and 4/3, the Lie-algebra residuals ~1e-16, Wilson-loop unitarity ~1e-15.- No srmech-side call consumer —
sm/so8only mention gauge in comments (so8 explicitly does NOT usesu3_structure_constants); no producer-flip boundary needed.
test_qm_gauge.py was rewritten numpy-FREE — Hermiticity / trace / normalization (tr(T^a T^b)=½δ^{ab}) / Casimir / unitarity by direct Mat-entry arithmetic + the native mat_matmul, a deterministic LCG for the multi-segment Wilson loop, and an analytic diagonal-λ³ exp oracle, no np (per [[feedback_test_for_numpy_free_module_must_itself_be_numpy_free]]). The gauge cell in test_qm_cascade_routing_rc33.py is numpy-free too (independent analytic oracle + native mat_matmul unitarity). CI-GUARD: the must-raise example stays propagators (still a numpy carrier — gauge ≠ propagators); gauge added to the pure-wheel flipped-and-exercised list. Carrier-only — no math-ledger change; no new public op (describe()["tools"]["total"] stays 293); ABI 3; no C change. All 5 SSOT version locations bumped. Remaining 9 carriers: potentials / so8 / octonion / triality / propagators / pseudo_hermitian qm tail + mcp/_coercion + spectral/__init__ + ica_jade.
[0.7.5rc118] - 2026-06-12¶
qm/relativistic.py (Dirac γ-matrix algebra) goes numpy-FREE (CEIL_NUMPY_CARRIER 11 → 10). Carrier-removal #564 — the relativistic-QM layer (γ-matrices / γ₅ / Weyl projectors / charge conjugation / Klein-Gordon / Dirac operator). The 4×4 operators are held in the framework-native Mat, assembled from the numpy-free Pauli 2×2 Mat blocks; every matrix product routes through the Class-L mat_matmul, residual norms through mat_norm, and the Klein-Gordon dispersion through the Class-N rational.sqrt — no numpy.
srmech/qm/relativistic.pydropsimport numpy as np(and thedense_dot_real/dense_matmul_complex/dense_matvec_real/dense_normcarrier imports). The Dirac γ-matrices are built from the rc115 numpy-freespin.pauli_*2×2Matblocks via a_block4helper (the numpy-freenp.blockreplacement) — so the rc115.to_numpy()boundary coercion that this module carried is removed.gamma_5/charge_conjugation_matrixusemat_matmul;clifford_residualsusesmat_matmul+mat_norm;minkowski_metricis a realMat; 4-momenta are plain float sequences (four_momentum_squared/klein_gordon_dispersioncompute the bilinear / dispersion numpy-free). Verified runs numpy-genuinely-absent: the Cl(1,3) Clifford residuals{γ^μ,γ^ν}=2η^{μν}Iare exactly 0.0.- PRODUCER-FLIP:
propagators(still a numpy carrier) consumesdirac_operator_momentum_space+minkowski_metric, so those 3 callsites coerce.to_numpy()at the boundary (the legit bridge inside a still-numpy carrier, removed when propagators flips).
test_qm_relativistic.py was rewritten numpy-FREE — Clifford-algebra / chirality-projector / charge-conjugation (C γ C^{-1} = -γ^T via the native mat_solve for C^{-1}) / Klein-Gordon identities, all mat_matmul/mat_norm + direct Mat-entry comparison, no np oracle (per [[feedback_test_for_numpy_free_module_must_itself_be_numpy_free]]). The two relativistic-producer calls in test_qm_propagators.py bridge .to_numpy() (testing the still-numpy propagators). CI-GUARD: rc117 named relativistic as the pure-wheel must-raise example — moved it to propagators (still numpy) and added relativistic to the flipped-and-exercised list (the recurring lesson). Carrier-only — no math-ledger change; no new public op (describe()["tools"]["total"] stays 293); ABI 3; no C change. All 5 SSOT version locations bumped. Remaining 10 carriers: gauge / potentials / so8 / octonion / triality / propagators / pseudo_hermitian qm tail + mcp/_coercion + spectral/__init__ + ica_jade.
[0.7.5rc117] - 2026-06-11¶
qm/single_particle.py goes numpy-FREE (CEIL_NUMPY_CARRIER 12 → 11). Carrier-removal #564 — the design-heavy submodule split out of rc116 (TDSE/TISE/Heisenberg/commutator/density-matrix/Liouville-von-Neumann). The single-particle dynamics layer now holds its working matrices in the framework-native Mat carrier and composes the eigenbasis time-evolution operator V·diag(e^{-iλt})·Vᴴ entirely from the Class-L mat_* family + the Class-N rational.cexp Euler cascade — no numpy, no np.exp.
srmech/qm/single_particle.pydropsimport numpy as np(and thedense_matmul_complex/dense_matvec_complex/dense_outer_complex/hermitian_eigendecompose/elementwise_transcendentalcarrier imports). Matrices (H,A,ρ,V) areMat; state vectors are plaincomplexlists (Matis 2-D only). Every linear-algebra step routes throughmat_matmul(matmul, matvec-via-column-Mat, outer-product-via-column·row) andmat_hermitian_eigendecompose(the eigenbasis); the per-mode phasee^{-iλt}isrational.cexp(-λt)(Eulercos + i·sin, NOTnp.exp).tise_solveis now a thin pass-through tomat_hermitian_eigendecompose(returns the(n,1)real eigvalMat+(n,n)complex unitaryMat). The only srmech-side consumer istool_schemaregistration metadata (strings, not a call — no boundary coercion, unlike rc115's producer-flip).- Verified runs numpy-genuinely-absent (
_BLOCK_NUMPYmeta-path): TDSE preserves norm to 1.1e-16, TISE unitarity 7.4e-16 on the native path.
Three test files rewritten numpy-FREE: test_qm_single_particle.py asserts canonical physical identities (norm / energy / trace / hermiticity / unitarity / purity preservation; eigenstate-phase evolution) with a deterministic numpy-free PRNG building Hermitian Mats and the rational cascade as the only oracle — no np, no numpy reference (per [[feedback_test_for_numpy_free_module_must_itself_be_numpy_free]]); the two single_particle tests in test_qm_cascade_routing_rc33.py (reconstruction + unitarity + closed-form TDSE, all Mat/rational); and the cross-module harmonic-oscillator TDSE test in test_qm_potentials.py (potentials' still-numpy H bridged to Mat once at the flipped-op boundary, the TDSE assertion then numpy-free). Carrier-only — the math was already cascade-routed, so no math-ledger change; no new public op (describe()["tools"]["total"] stays 293); ABI 3; no C change. All 5 SSOT version locations bumped incl. the scaffolding pin. Remaining 11 carriers: the so8/octonion/triality/relativistic/propagators/pseudo_hermitian/potentials/gauge qm tail + mcp/_coercion + spectral/__init__ + ica_jade.
[0.7.5rc116] - 2026-06-11¶
qm/sm.py (Standard Model) goes numpy-FREE (CEIL_NUMPY_CARRIER 13 → 12). Carrier-removal #564, the next qm-submodule consumer flip after rc115's spin/bell. The roadmap paired single_particle + sm for this rc; per the "design-heavy — don't rush, split on wobble" discipline, sm (clean) ships now and the design-heavy single_particle (matrix-exponential V·diag(e^{−iEₜ})·V† via a complex diagonal from Class-N rational trig) is split to its own later rc.
srmech/qm/sm.pydropsimport numpy as np(and thedense_matmul_complex/dense_normcarrier import). Every scalar electroweak op already routed through the Class-Nrationalcascade (Higgs VEV√(μ²/2λ); W/Z masses; Weinberg residual viarational.cos; Yukaway·v/√2) — those are unchanged. The only numpy was the flavour sector:ckm_matrixnow returns an exact-cascade 3×3 complexMat(the mixing-anglecos/sinviarational, the CP phasee^{±iδ}viarational.cexp, assembled by entrywise list build);ckm_unitarity_residualcomputesV·Vᴴvia the nativemat_matmul, the entrywise− Ias plainMat-entry subtraction, and the Frobenius norm via the rc114 Class-Nmat_norm(no numpy). Verified numpy-absent: a unitary CKM gives residual ≈3.8e-16.smis a leaf (no consumers — no producer-flip boundary needed, unlike rc115's relativistic/gauge).
test_qm_sm.py was rewritten numpy-FREE — no np oracle: unitarity via mat_matmul + a _max_dev_from_identity direct Mat-entry comparison (numpy is not a validation reference per [[feedback_test_for_numpy_free_module_must_itself_be_numpy_free]]). Verified by passing the file with numpy blocked at the meta-path. Carrier-only — the math was already cascade-routed, so the math ledger is untouched; no new public op (describe()["tools"]["total"] stays 293); ABI 3; no C change. Version bumped at all 5 SSOT locations incl. the scaffolding pin. Remaining 12 carriers: single_particle + the so8/octonion/triality/relativistic/propagators/pseudo_hermitian/potentials/gauge qm tail + mcp/_coercion + spectral/__init__ + ica_jade.
[0.7.5rc115] - 2026-06-11¶
First qm-submodule CONSUMER carrier-flips — qm/spin.py + qm/bell.py go numpy-FREE (CEIL_NUMPY_CARRIER 15 → 13). Carrier-removal #564, the first consumers to flip on the rc114 mat_norm/mat_dot_* + the rc74 mat_hermitian_eigendecompose foundation. Both modules were already cascade-routed for the math; this removes numpy as the carrier so they import and run with numpy genuinely absent.
srmech/qm/spin.pydropsimport numpy as np.pauli_matrices()/pauli_identity()return exact 2×2 complexMat(entries in{0, ±1, ±i}— no float approximation);pauli_spin_operator(direction)accepts any 3-sequence, normalises via the rc114mat_norm, and builds the 2×2Matby entrywise list arithmetic (noabs()— Class-K magnitude is themat_norm√(Σ|·|²));pauli_clifford_residuals()forms every Pauli product with the nativemat_matmul, combines entrywise as flat complex lists, and measures withmat_norm(Clifford anticommutator + commutator residuals exactly0.0).srmech/qm/bell.pydropsimport numpy as np(incl. two stale-> np.ndarrayannotations →-> "Mat", and a docstring that cited "numpy's Jacobi floor" as the accuracy reference).chsh_pauli_combination()/chsh_operator()returnMatbuilt via the numpy-freekroncascade + entrywise list arithmetic (the1/√2phase via Class-Nrational.sqrt);chsh_pauli_combination_norm()uses the EXACT integereigvals_exacton the {0,±1} 4×4 (Tsirelson primary identity= 2at residual exactly 0);operator_norm()/chsh_operator_norm()go through the unconditionally-numpy-freemat_hermitian_eigendecompose(Class-K max-|λ|via sign-branch, noabs()).srmech/qm/__init__.pyrelaxes the eager_require_numpy("srmech.qm")subpackage gate to LAZY per-submodule__getattr__(the rc71 signal_processing precedent /[[feedback_carrier_ratchet_misses_require_numpy_subpackage_gates]]): the flippedspin/bellimport with numpy absent, while the not-yet-flipped qm modules still surface the actionable[scientific]hint (not a bare numpyImportError).- Producer-flip boundary coercion —
qm/relativistic.py(Dirac γ-matrices) andqm/gauge.py(SU(2) generators) are STILL numpy carriers (they flip in their own later #564 rcs) that consumespin.pauli_matrices()/pauli_identity(). Since those now returnMat, the two consumers coerce the producer to ndarray at the import boundary via the lossy export bridge (p.to_numpy()) — the legitimate use of the bridge: a numpy module receiving a numpy-freeMat. The boundary disappears when each module itself goes numpy-free. (Without this,np.block([[I2,…],[…,-I2]])over aMatraisedTypeError.)
test_qm_spin.py + test_bell_chsh.py were rewritten numpy-FREE — no np oracle, no .to_numpy() (numpy is not a validation reference per [[feedback_no_numpy_rosetta_peer_continuous_float_error_collecting]]): eigenvalues come from mat_hermitian_eigendecompose, Kronecker from the kron cascade, and every matrix check is a direct Mat-entry comparison — so the numpy-absent install runs its own tests (verified by collecting + passing both files with numpy blocked at the meta-path). Carrier-only — the math was already cascade-routed, so the math ledger is untouched; no new public op (describe()["tools"]["total"] stays 293); ABI 3; no C change. Version bumped at all 5 SSOT locations incl. the scaffolding pin. Remaining 13 carriers: the qm matrix tail (gauge/octonion/potentials/propagators/pseudo_hermitian/relativistic/single_particle/sm/so8/triality), mcp/_coercion, spectral/__init__, ica_jade.
[0.7.5rc114] - 2026-06-11¶
FOUNDATION for the carrier-ratchet → 0 endgame — numpy-free mat_norm / mat_dot_real / mat_dot_complex (CEIL_NUMPY_CARRIER stays 15). Carrier-removal #564, the first of three spine foundations (with the planned exact-RREF-rank + numpy-free octonion-mult) that gate the remaining qm/so8/spectral consumer-flips. A carrier-ratchet-to-zero workflow ground-truthed the remaining 15 carriers against source and caught the load-bearing trap: dense_norm / dense_dot_real / dense_dot_complex are numpy CARRIERS (they call np.ascontiguousarray / np.iscomplexobj / the elementwise-multiply cascade over numpy arrays) and RAISE on a numpy-absent install — the rc70 runnable ≠ loadable trap. Nothing in the tree computed ‖x‖ or a·b over the numpy-free carriers, so the qm Clifford / unitarity / η-Hermiticity residuals and so8's Gram-Schmidt could not flip.
srmech/amsc/laplacian.pyadds three genuinely numpy-free reductions over theMat/HVcarriers (added to both__all__exports):mat_norm(x)=√(Σ|xᵢ|²)(vector 2-norm / matrix Frobenius) via a pure-PythonΣ|xᵢ|²(complex|z|² = z.real²+z.imag², noabs(), nomath.hypot) then the libm-free Class-Nrational.sqrtroot — Class N ∘ Class M;mat_dot_real(a,b)/mat_dot_complex(a,b)= plain bilinearΣ aᵢbᵢ(matching numpya·b, NOT the conjugatingvdot) over a pure-Python reduction. A shared_iter_mat_scalarsflattens aMat(interleaved-complex aware) /HV/ flat sequence row-major.
Value-faithful to dense_norm / dense_dot_* / the numpy peers to ~1 ULP (same float sum-of-products), verified both with numpy present AND in a fresh subprocess with numpy blocked at the meta-path (test_mat_norm_dot_rc114.py — the real fresh-import-numpy-free gate, stronger than a monkeypatch). Foundation only — no consumer flips, so CEIL_NUMPY_CARRIER stays 15; the math ledger is untouched (these are new numpy-free ops, not numpy removals). The three are registered public srmech.amsc.laplacian.* callables exactly like their dense_* peers, so per the registry checklist each gets a ToolEntry + a composition_of_c rosetta_classification.ndjson line, bumping describe()["tools"]["total"] 290 → 293 (seven count-tests updated). ABI 3; no C change. Version bumped at all 5 SSOT locations incl. the scaffolding pin.
[0.7.5rc113] - 2026-06-11¶
RBS-LM subpackage goes numpy-FREE — incidental-source RNG re-based onto our own (CEIL_NUMPY_CARRIER 17 → 15). Carrier-removal #564. srmech.rbs_lm (the F166 inference substrate: Class A∘M Klein-4 encode + iω₇ position + Class M retrieve, temperature-sampled) carried numpy only as an incidental deterministic source, never a correctness oracle — so per user direction ("use our own rng … rbs_lm is ours") the three numpy-RNG sites are swapped to the framework-native stdlib stream and the values re-based ONCE:
rbs_lm/substrate.pydropsimport numpy as np. The per-token vector seednp.random.default_rng(token_seed(word))→hdc.klein4_random(D, seed=token_seed(word))(the §22 numpy-optional stdlibrandom.Randompath); thenp.full(D, sector, uint8)sector key → a numpy-freebytes([sector]) * Dconstant (klein4_bindcoerces it); and the encode helpers +ContextSubstratenow return the framework-nativeHVcarrier end-to-end (no.to_numpy()).sim_k4_batchis the Class-Mhdc.klein4_similarityover each HV candidate (== the old(candidates == query).mean(axis=1)).rbs_lm/inference.pydropsimport numpy as np._softmaxroutes its per-element exp through the Class-Nrational.expcascade (NOT the numpy-carrierelementwise_transcendental);vocab_vecsis alist[HV](wasnp.stack); the learn-memory subsamplerng.choice(…, replace=False)→random.Random(learn_seed).sample; the infer samplergr.choice(…, p=p)→random.Random(seed).choices(weights=p). Distributions/probs are plain Pythonlists.rbs_lm/__init__.pyREMOVES the eager_require_numpy("srmech.rbs_lm")gate (per[[feedback_carrier_ratchet_misses_require_numpy_subpackage_gates]]: both layers must fall — the whole subpackage is now numpy-free, soimport srmech.rbs_lmsucceeds with numpy genuinely absent, no[scientific]extra).
Authorized one-time RNG re-base (numpy-as-incidental-source, not numpy-as-accuracy): stdlib MT19937 ≠ numpy PCG64, so the underlying Klein-4 bytes + sampled sequences change, but every test asserts an RNG-INDEPENDENT structural property (determinism, the XOR-sector relationship s₁ == s₀^1, self-similarity == 1.0, bigram-legality, same-seed determinism) — no test pins a specific byte value, so the re-base needs no value oracle. test_rbs_lm.py migrates the ndarray assertions (.shape / np.array_equal / np.unique) to HV-native (len / == / .tolist). Carrier-only — the encode math was already the cascade (hdc.klein4_*), so the math-ratchet ledger is untouched; the A-N transcendental ratchet stays green (rational.exp IS the cascade). No new public op (describe()["tools"]["total"] stays 290, classes 2); ABI 3; no C change. Version bumped at all 5 SSOT locations incl. the scaffolding pin. Remaining 15 carriers: the qm matrix layer (TOML [class] reframe / lossy-peer delete — not per-module flips), mcp/_coercion, spectral/__init__, ica_jade (numpy-as-accuracy: eigh), and the so8/triality matrix-algebra tail (numpy-as-accuracy: matrix_rank).
[0.7.5rc112] - 2026-06-11¶
Carrier-ratchet ACCURACY fix — the_one was a false positive (CEIL_NUMPY_CARRIER 19 → 18 → 17). Carrier-removal #564, opening the qm-layer phase. NOT a carrier removal — a correction to the ratchet's measurement:
amsc/cascade/one.py(the exact-rationalthe_onegenerator) is numpy-FREE at import — its only numpy is a LAZYrequire_numpyinside the opt-inOne.to_numpy/One.to_matrixfloat exports. But the ratchet's scanner used a naiveline.startswith("import numpy")over every line, and a docstring that wrapped to"import numpy lazily (the …"at column 0 tripped it, sothe_onewas wrongly counted as a carrier.- The scanner is hardened to a real-import regex (
_is_numpy_import): it matchesimport numpy/import numpy as np/import numpy.sub/from numpy[.sub] import …(optionally trailed by a comment) and notimport numpy <word>…prose.the_onedrops out of the count; the 17 genuine carriers (all realimport numpy as np) are unchanged. Theone.pydocstring is also reworded defensively.
Honest down-only decrement (the ratchet now reflects reality — the_one was never a carrier). No source op changed; describe()["tools"]["total"] stays 290, classes 2; ABI 3; no C change. Version bumped at all 5 SSOT locations incl. the scaffolding pin. The remaining 17 are the real carriers: the qm matrix layer (TOML [class] reframe / lossy-peer delete) + rbs_lm + mcp/_coercion + spectral/__init__ + ica_jade.
[0.7.5rc111] - 2026-06-11¶
Fourteenth CONSUMER carrier-flip — jpeg goes numpy-FREE, the LAST clean DSP carrier flip (CEIL_NUMPY_CARRIER 19 → 18). Carrier-removal #564. The block-DCT image compressor (Class L ∘ K ∘ B):
- The block transform already ran numpy-free through the rc104
dct.op(returns a list-of-lists);jpegnow carries the 2-D image as nested Pythonlists end-to-end. The canonical luminance quantisation table becomes a plain list-of-lists; the Wallace quality scaling is per-element (int(...)is exact floor for the non-negative(luma·scale + 50)/100); the Class-K quantise isround(coeff / qt)(Pythonroundis round-half-to-even — bit-identical tonp.round); and the encode/decode block loops index nested lists directly (the_dctnp.asarrayconsumer-boundary wrapper is removed). - Drops
import numpy as np. Encode returns(quant_blocks, shape, quant_table)withquant_blocksalistof 8×8 integer list-of-lists andquant_tablea list-of-lists; decode returns a 2-Dlist(both were ndarrays). Inputs coerce numpy-free viatolist().
Value-faithful to the prior matrix path (the DCT basis is the Class-N rational.cos cascade, value-faithful to ~1e-9): verified by an encode→decode round-trip (RMS ≈ 0.58 at quality 75, quantisation-bounded) and a numpy-absent subprocess run. The test_jpeg_smoke len(quant_blocks) == 4 assertion is unchanged. Carrier-only — the math was already cascade-routed, so the math-ratchet ledger is untouched. No new public op (describe()["tools"]["total"] stays 290, classes 2); ABI 3; no C change. Version bumped at all 5 SSOT locations incl. the scaffolding pin.
With jpeg flipped, the clean-DSP carrier-flip phase of #564 is complete; the remaining carriers are the qm matrix layer (resolved via the TOML [class] reframe / lossy-peer deletion, the rc75 Hurwitz precedent — not per-module flips) plus a linalg-consolidation pass to retire the duplicate mat_* / matrix_cascades SVD paths.
[0.7.5rc110] - 2026-06-11¶
Twelfth + thirteenth CONSUMER carrier-flips — the two Path B DSP duals go numpy-FREE (CEIL_NUMPY_CARRIER 21 → 19). Carrier-removal #564. Both are clean leaf flips of ops whose Path A counterparts already went numpy-free (rc80 matched_filter, rc87 wiener):
path_b_ops/matched_filter(Class A∘C∘M form-function cross-correlation) dropsimport numpy as npand delegates straight to the rc79 numpy-free_dsp.correlate(the FFT-convolution-theorem correlationsum_n a[n+k]·conj(v[n]), which coerces both inputs to 1-D lists and returns a list). The priornp.asarrayinput-coerce + return-wrap are gone; the op returns alist(was an ndarray).path_b_ops/wiener(Class L∘N cyclic-graph-Laplacian eigenbasis + rational MMSE gain) takes the rc87 closed-form list-comprehension form:_sc.fftreturnsList[complex]numpy-free, the per-bin power is|X|² = X.real² + X.imag²(noabs()), thenp.maximum(·, 1e-30)eps-floors become the builtinmax, the Class-N rational gainS/(S+N)and the spectral product are explicit comprehensions,_sc.ifft→ real part. Returns alistof float.
Both flips are carrier-only — the math was already cascade-routed (_dsp.correlate / _sc.fft/_sc.ifft), so only the numpy carriers (np.asarray / np.maximum / np.real) go. The math-ratchet ledger is untouched. The Path A↔Path B D1-equivalence smoke (test_signal_processing_path_b_mvp) keeps passing (list vs list). No new public op (describe()["tools"]["total"] stays 290, classes 2); ABI 3; no C change. Version bumped at all 5 SSOT locations incl. the scaffolding pin.
[0.7.5rc109] - 2026-06-11¶
Eleventh CONSUMER carrier-flip — mimo_svd goes numpy-FREE (CEIL_NUMPY_CARRIER 22 → 21). Carrier-removal #564. The first consumer to route onto the rc108 mat_svd Mat-carrier foundation:
- The MIMO channel-matrix SVD (
H = U·S·Vᴴ, Class L) dropsimport numpy as npandnp.linalg.svdentirely. It coerces the channel matrix numpy-free (tolist()covers ndarray ANDMat), wraps it inMat.from_rows(..., is_complex=True), callslaplacian.mat_svd(GramAᴴA→mat_hermitian_eigendecompose→√λ+A·v/σ+ orthonormal null completion), and returns plain Pythonlists —U(n_rx, n_rx)list-of-rows,Sdescendinglist,Vh(n_tx, n_tx)list-of-rows (were ndarrays). - Value-faithful, NOT bit-identical (per
[[feedback_cascade_svd_nullspace_accuracy_not_route_matrix_rank]]): SVD is non-unique in the per-pair phase and the degenerate / null subspace, so correctness is the reconstructionH ≈ U·diag(S)·Vᴴ+ unitarity ofU/Vh+ singular-value match to NumPy. Differential-verified over 100 cases (real/complex × square/tall/wide × full/rank-deficient): 0 reconstruction failures, 0 singular-value failures.
Because the op also removes its np.linalg.svd, this decrements both ledgers: CEIL_NUMPY_CARRIER 22 → 21 and the math-ratchet CEIL_LINALG_FFT 23 → 22. The test_mimo_svd_smoke .shape checks move to isinstance(U, list) / len. The rc71 numpy-free-reachable exemplar (the still-numpy op that must raise the clean [scientific] hint) moves mimo_svd → ica_jade — ica_jade's np.linalg.eigh is numpy-as-ACCURACY (not just a carrier), so it stays gated. No new public op (describe()["tools"]["total"] stays 290, classes 2); ABI 3; no C change. Version bumped at all 5 SSOT locations incl. the scaffolding pin.
[0.7.5rc108] - 2026-06-11¶
mat_svd foundation — numpy-free FULL SVD over the Mat carrier (carrier-removal #564, Mat-bridge foundation #5). The op mimo_svd has waited on this; it does NOT decrement CEIL_NUMPY_CARRIER (still 22) — it's the foundation the next consumer flip routes onto:
laplacian.mat_svd(A: Mat) -> (U: Mat, S: list[float], Vh: Mat)mirrors thesvd(A, full_matrices=True)shape contract:U(m,m)complex,Sdescendingmin(m,n),Vh(n,n)complex, soA = U[:, :k]·diag(S)·Vh[:k, :]. The right vectors are eigenvectors of the Hermitian PSD GramAᴴAviamat_hermitian_eigendecompose(reordered descending);S = √λ(the Class-Nrational.sqrt, libm-free); the left vectors areuⱼ = A·vⱼ/σⱼforσⱼabove the rank tolerance, with an orthonormal modified-Gram–Schmidt completion of the left-nullspace block. Unconditionally numpy-free (composes the native-backedmat_matmul+mat_hermitian_eigendecompose; pure-Python completion).- Value-faithful, NOT bit-identical (per
[[feedback_cascade_svd_nullspace_accuracy_not_route_matrix_rank]]): SVD is non-unique (a per-pair phase, and a free unitary basis inside a degenerate-σ / null subspace), so correctness is pinned by reconstruction (A ≈ U·diag(S)·Vᴴ), unitarity (UᴴU ≈ I,Vh·Vhᴴ ≈ I), and singular-value match to NumPy — verified across real/complex × square/tall/wide × full-rank/rank-deficient. The rank tolerance isσ_max·max(m,n)·1e-6(relative to the Gram eigen-route's ~1e-7·σ_max small-σ floor, NOT machine-eps) so a sub-floor σ routes its U column through the completion instead of lettingA·v/σamplify the error.
Registered as a ToolEntry (like its mat_* siblings) — describe()["tools"]["total"] 289 → 290 (the five count-tests bump in lockstep); __all__ + rosetta composition_of_c bucket added. CEIL_NUMPY_CARRIER unchanged at 22; math-ratchet ledger untouched; ABI 3; no C change. Version bumped at all 5 SSOT locations incl. the scaffolding pin.
[0.7.5rc107] - 2026-06-11¶
Tenth CONSUMER carrier-flip — mlse goes numpy-FREE (CEIL_NUMPY_CARRIER 23 → 22). Carrier-removal #564. The Viterbi-trellis channel equaliser (Class L ∘ K), no np.linalg:
- The trellis tables — transition / emission / initial log-prob — become pure-Python list-of-lists built with the Class-N
rational.logcascade (uniform−log Atransition priors; uniform−log n_statesinitial). The per-branch metric is an explicit−|obs − expected|²multiply-add (squared distance, monotone in|·|, so nosqrtand noabs()). - The no-ISI fast path (
memory == 0) is a per-sample squared-distance argmin overobs − taps[0]·alpha(strict<, first-minimum tie-break matchingnp.argmin), replacing theelementwise_hypotcarrier. - The trellis search itself delegates to the already-numpy-free
viterbi.op(rc83), which returns a plainlist. Inputs coerce numpy-free viatolist(); the op now returnslist[int](was an ndarray).
Differential-verified BIT-IDENTICAL to the numpy reference over 180 cases (BPSK + QPSK × channel-memory {0, 1, 2} × random observations; 0 sequence mismatches). The test_mlse_smoke .shape == (4,) moves to isinstance(syms, list) and len(syms) == 4. CEIL_NUMPY_CARRIER 23 → 22 (down-only ratchet). The math-ratchet ledger is untouched (the op now uses no numpy; mlse only appears in math-ratchet comments, none enforced). mlse's rosetta_classification.ndjson bucket is unchanged. No new public op (describe()["tools"]["total"] stays 289, classes 2); ABI 3; no C change. Version bumped at all 5 SSOT locations incl. the scaffolding pin.
[0.7.5rc106] - 2026-06-11¶
Ninth CONSUMER carrier-flip — psk_qam goes numpy-FREE (CEIL_NUMPY_CARRIER 24 → 23). Carrier-removal #564. The fsk-cousin constellation mapper (Class I ∘ K), no np.linalg:
- The PSK constellation points
e^{i·2π·k/M}route through_exp_i(the Class-Nrational.cos/rational.sincascade over the substrate-native_PI=pi_cascade_digits(30), native libm-free) — bit-faithful to the priorelementwise_transcendental(·, 'exp_i')path. The QAM grid is built from pure-Python Gray-coded levels (√Mvia the Class-Nrational.sqrt), replacingnp.meshgrid(...).flatten()with a row-majorcomplex(levels[col], levels[row])comprehension (byte-faithful ordering). - The demod nearest-neighbour decision is an argmin over the squared Euclidean distance
|received − const|²(squared distance is monotone in|·|, so the decision is identical to the priorargmin(hypot(re, im))— and now needs nosqrt/ nohypot/ noabs(); strict<keeps the first-minimum index, matching the priornp.argmintie-break). The top-levelimport numpy as npis gone; inputs coerce numpy-free viatolist(); modulate returnslist[complex], demodulate returnslist[int](were ndarrays).
The test_psk_qam_smoke .shape == (4,) / np.iscomplexobj checks move to isinstance(points, list) / len / isinstance(z, complex); the rc52 demod round-trip is unchanged (decision is identical). CEIL_NUMPY_CARRIER 24 → 23 (down-only ratchet). The math-ratchet ledger is untouched (the op now uses no numpy at all; psk_qam only appears in math-ratchet comments, none of which are enforced counts). psk_qam's rosetta_classification.ndjson bucket is unchanged. No new public op (describe()["tools"]["total"] stays 289, classes 2); ABI 3; no C change. Version bumped at all 5 SSOT locations incl. the scaffolding pin.
[0.7.5rc105] - 2026-06-11¶
Eighth CONSUMER carrier-flip — vector_quantisation goes numpy-FREE (CEIL_NUMPY_CARRIER 25 → 24). Carrier-removal #564. The cleanest remaining consumer — nearest-codebook lookup (Class E ∘ M ∘ B), no np.linalg:
- The nearest-neighbour query becomes a pure-Python argmin over the squared Euclidean distance
Σ_j (x_j − c_j)²(the|x|² − 2x·c + |c|²matmul cross-term trick is unnecessary for an argmin, sodense_matmul_realis dropped). Inputs coerce numpy-free viatolist()(a single 1-D vector is accepted as one row). The top-levelimport numpy as npis gone; encode returnslist[int], decode returns a list-of-rows (were ndarrays). - Differential-verified: the encode argmin is bit-identical to numpy's
argmin(Σ(x−c)², axis=1)over 200 random(n_codes, d, n_vec)configs (0/1262 mismatches); decode round-trips.
The single idx.shape == (10,) smoke moves to len(idx) == 10. CEIL_NUMPY_CARRIER 25 → 24 (down-only ratchet). The math-ratchet ledger is untouched (np.sum/np.argmin/np.asarray + the named dense_matmul_real match no counted pattern — and the op now uses no numpy at all). vector_quantisation's rosetta_classification.ndjson bucket stays python_only_debt. No new public op (describe()["tools"]["total"] stays 289, classes 2); ABI 3; no C change. Version bumped at all 5 SSOT locations incl. the scaffolding pin.
[0.7.5rc104] - 2026-06-11¶
Seventh CONSUMER carrier-flip — dct goes numpy-FREE (CEIL_NUMPY_CARRIER 26 → 25). Carrier-removal #564. The DCT-II / DCT-III (the JPEG / audio-codec cosine transform) flips numpy-free — the involved one of the batch, because it also has a jpeg consumer:
- The cosine basis matrix
_dct_matrixis built directly as a list-of-lists through the Class-Nrational.coscascade over the substrate-native_PI = pi_cascade_digits(30)source (nonp.cos/np.pi); value-faithful to thenp.cosmatrix to ~1e-9. The transform is a pure-Python matvec (1-D) / per-axis row|column transform (2-D,axis0/1/-1 — the jpeg block case). The oldscipy.fft.dctfast-path is dropped (it required numpy as a carrier); the cascade DCT-matrix path IS the substrate-native realisation. - DCT-III parity fix: scipy's
norm=NoneDCT-III weights thex_0term by 1, not 2 (y_k = x_0 + 2·Σ_{j≥1} M[k][j] x_j), making it the exact inverse of DCT-II. The prior matrix-fallback's blanket2.0·Σdoubled it — a latent inconsistency that was masked because the op preferred the scipy path. The numpy-free_transformnow matches scipy exactly for both types. jpegconsumer:jpegcallsdct_opfor its block DCT-II encode / DCT-III decode and expects ndarrays. jpeg is still a numpy carrier (it flips in its own later rc), so it now coerces dct's list return back to ndarray at a thin_dct(...)boundary wrapper (np.asarray). Values are bit-identical to the prior matrix path.- Differential-verified: dct 1-D and 2-D (both axes), DCT-II and DCT-III, all ~1e-14 vs
scipy.fft.dct(..., norm=None); thejpegencode→decode round-trip recovers the image with RMSE 1.2 / 0.25 / 0.025 at quality 50 / 90 / 99 (clean lossy-quant behaviour). Ripples:test_dct_smoke/test_dct_op_stable_and_invertibleX.shape == (8,)→len,np.all(np.isfinite)→all(math.isfinite),M @ x→ an explicit pure-Python matvec; the rc33_dct_matrixbasis tests'cascade.shape→len-based checks (np.allclose(cascade, reference)still coerces the list).
CEIL_NUMPY_CARRIER 26 → 25 (down-only ratchet; only dct drops its import numpy, jpeg keeps its own). The math-ratchet ledger is untouched (rational.cos + pure-Python; no counted linalg/fft/matmul/ufunc patterns). No new public op (describe()["tools"]["total"] stays 289, classes 2); ABI 3; no C change. Version bumped at all 5 SSOT locations incl. the scaffolding pin.
[0.7.5rc103] - 2026-06-11¶
Sixth CONSUMER carrier-flip — fsk goes numpy-FREE (CEIL_NUMPY_CARRIER 27 → 26). Carrier-removal #564. The FSK (frequency-shift-keying) modulator/demodulator — educational civilian-comms textbook reference per the trauma-informed defensive scope — flips numpy-free:
- The per-symbol tone phases
e^{i·2π·f·t}route off the numpy-carrierelementwise_transcendental(·, "exp_i")onto a per-element Class-N_exp_i(θ) = complex(rational.cos(θ), rational.sin(θ))over the substrate-native_PI = pi_cascade_digits(30)source (native libm-free, bit-faithful to the prior path). The demodulator correlator bank becomes a pure-Python complex matveccorr_k = Σ_j tone[k][j]·conj(window[j])whose nearest-tone decision isargmax|corr|²— monotone in|corr|, so nosqrtand noabs()(drops thedense_matvec_complex+elementwise_hypotcarriers too). Inputs coerce numpy-free viatolist(). The top-levelimport numpy as npis gone; modulate returnslist[complex], demodulate returnslist[int](was ndarrays). - Differential-verified: modulate is bit-faithful to the
e^{i2πft}reference (max-err ~1e-16); a noiseless demod round-trip recovers the symbol stream exactly, and a 0.01-noise sweep over 300 random(M, samples_per_symbol)configs misclassifies <0.1% of symbols (noise-edge, not the routing). The onewaveform.shape == (4*8,)smoke moves tolen(waveform) == 4*8.
CEIL_NUMPY_CARRIER 27 → 26 (down-only ratchet). The math-ratchet ledger is untouched (fsk's np.pi constant / np.conj / np.argmax + the named elementwise_*/dense_matvec_complex helpers match none of the counted linalg/fft/matmul/ufunc patterns — and the op now uses no numpy at all). fsk's rosetta_classification.ndjson bucket stays python_only_debt (still a Python op with no C peer). No new public op (describe()["tools"]["total"] stays 289, classes 2); ABI 3; no C change. Version bumped at all 5 SSOT locations incl. the scaffolding pin.
[0.7.5rc102] - 2026-06-11¶
Fifth CONSUMER carrier-flip — map_ml goes numpy-FREE (CEIL_NUMPY_CARRIER 28 → 27). Carrier-removal #564. The same real-solve family as lmmse: the linear-Gaussian MAP / ML estimator x_hat = (Aᵀ R_v⁻¹ A + R_x⁻¹)⁻¹ (Aᵀ R_v⁻¹ y + R_x⁻¹ μ) (Kay 1993 §7 / §11):
- The two covariance inverses (
R_v⁻¹, andR_x⁻¹on the MAP branch) route off the numpy-carrierdense_solve(M, np.eye(n))onto the nativemat_solve(Mat, identity_Mat)over realMats (the inverse IS the solve against the identity; ridessrmech_dense_solve_f64).Aᵀ R_v⁻¹ridesmat_matmul, the normal-equation matrixAᵀ R_v⁻¹ Aridesmat_matmul, and theAᵀ R_v⁻¹ y/R_x⁻¹ μmatvecs + theM + R_x⁻¹precision add are pure-Python sums. Inputs (y,A,R_noise, optionalR_prior/mean_prior) coerce numpy-free viatolist(). The top-levelimport numpy as npis gone; both branches returnlist[float](was an ndarray). - Differential-verified value-faithful to the numpy-present closed-form reference over 200 random
(m, n)trials (ML max-err ~7e-12 — the normal-equations conditioning — and MAP max-err ~5e-14). Thex_hat.shape == (2,)smoke (baseline) andx_ml.shape/x_map.shape == (n,)+np.all(np.isfinite(...))asserts (rc64 test) move tolen(...)+all(math.isfinite(v) ...); the rc64 residual-check"dense_solve(" in txtbecomes"mat_solve(" in txt.
CEIL_NUMPY_CARRIER 28 → 27 (down-only ratchet). The math-ratchet ledger is untouched (mat_solve/mat_matmul + np.asarray analogues are not counted linalg/fft/matmul/ufunc patterns — here the op uses none of numpy at all). No new public op (describe()["tools"]["total"] stays 289, classes 2); ABI 3; no C change. Version bumped at all 5 SSOT locations incl. the scaffolding pin.
[0.7.5rc101] - 2026-06-11¶
Fourth CONSUMER carrier-flip — lmmse goes numpy-FREE (CEIL_NUMPY_CARRIER 29 → 28). Carrier-removal #564. A different sub-shape from the eigendecomposition consumers: the real-valued linear MMSE estimator x_hat = mean_x + R_xy·R_yy⁻¹·(y − mean_y):
- The Class-L gain solve routes off the numpy-carrier
dense_solveonto the nativemat_solveover a realMat— solvingR_yyᵀ·Z = R_xyᵀforZsoK = Zᵀ = R_xy·R_yy⁻¹(the solve ridessrmech_dense_solve_f64); theK·(y − mean_y)estimate becomes a pure-Python matvecmx[i] + Σ_j Z[j,i]·(y_j − my_j). Inputs (the vector + the two covariance matrices + the optional means) coerce numpy-free viatolist(). The top-levelimport numpy as npis gone;opreturnslist[float](was an ndarray). - Differential-verified value-faithful to the numpy-present reference. The one
x_hat.shape == (1,)smoke moves tolen(x_hat) == 1.
CEIL_NUMPY_CARRIER 29 → 28 (down-only ratchet). The math-ratchet ledger is untouched (dense_solve/dense_matvec_complex + np.asarray/np.zeros/np.ascontiguousarray match none of the counted patterns). No new public op (describe()["tools"]["total"] stays 289, classes 2); ABI 3; no C change. Version bumped at all 5 SSOT locations incl. the scaffolding pin.
[0.7.5rc100] - 2026-06-11¶
Third CONSUMER carrier-flip — heat_kernel goes numpy-FREE (CEIL_NUMPY_CARRIER 30 → 29). Carrier-removal #564. The graph heat-kernel denoiser exp(−tL)·signal = V·diag(exp(−tλ))·Vᴴ·signal flips numpy-free:
- The Laplacian eigendecomposition routes off the numpy-carrier
hermitian_eigendecomposeonto the nativemat_hermitian_eigendecompose; the spectral filterg(λ) = exp(−t·λ)(real eigenvalues) routes off the numpy-carrierelementwise_transcendental(…, "exp")onto a per-bin Class-Nrational.exp(−t·λ_k)cascade (the scalar float-returningexp, which dispatches to nativesrmech_exp); the project (Vᴴ·signal) and reconstruct (V·(g ⊙ coeffs)) matvecs become pure-Python nested sums over the eigenvectorMat. The top-levelimport numpy as npis gone;opreturnslist[complex](was ndarray). - Differential-verified: mass conservation (the graph-Laplacian null-vector preserves the sum) holds to <1e-9, the output stays real for a real Laplacian + real signal, and an impulse diffuses onto its neighbour. The
out.shapesmoke asserts move tolen(out),out.real.sum()→sum(v.real for v in out),np.max(out.imag**2)→max(v.imag**2 for v in out),out.real[1]→out[1].real.
CEIL_NUMPY_CARRIER 30 → 29 (down-only ratchet). The math-ratchet ledger is untouched (dense_matvec_complex/elementwise_transcendental/np.asarray match none of the linalg/fft/matmul/ufunc patterns). No new public op (describe()["tools"]["total"] stays 289, classes 2); ABI 3; no C change. Version bumped at all 5 SSOT locations incl. the scaffolding pin.
[0.7.5rc99] - 2026-06-11¶
Second CONSUMER carrier-flip — music goes numpy-FREE (CEIL_NUMPY_CARRIER 31 → 30). Carrier-removal #564. The ESPRIT sibling on the completed Mat foundation: the MUSIC pseudospectrum DOA estimator flips numpy-free:
- The covariance eigendecomposition routes off the numpy-carrier
hermitian_eigendecomposeonto the nativemat_hermitian_eigendecompose(ascending eigenvalues → the noise subspace is the smallestM − n_sourceseigenvectors via a pure-Pythonsorted, nonp.argsort); the noise-subspace projectionEnᴴ·Aroutes offdense_matmul_complexonto the nativemat_matmul(Enᴴbuilt as a freshMatoverconj(eigvecs[i, noise_col])); the pseudospectrum1 / Σ_s |proj[s,k]|²is a pure-Python column loop (|z|² = re²+im², noabs()). The top-levelimport numpy as npis gone;opreturns alist[float](was an ndarray). - Differential-verified: the pseudospectrum peak lands at the true source spatial frequency (within one grid bin); all values positive. The
psd.shape == (...)smoke asserts move tolen(psd) == ...,np.all(psd > 0)→all(v > 0 for v in psd),np.argmax→ a pure-Python argmax. The rc71 "numpy op raises the clean[scientific]hint" exemplar moves frommusictomimo_svd(which still needs amat_svdfoundation op, so it stays the long-lived numpy-requiring exemplar).
CEIL_NUMPY_CARRIER 31 → 30 (down-only ratchet). The math-ratchet ledger is untouched (music's numpy — np.asarray/np.sum/np.argsort + the named dense_matmul_complex — matches none of the linalg/fft/matmul/ufunc patterns). No new public op (describe()["tools"]["total"] stays 289, classes 2); ABI 3; no C change. Version bumped at all 5 SSOT locations incl. the scaffolding pin.
[0.7.5rc98] - 2026-06-11¶
First CONSUMER carrier-flip on the completed Mat foundation — esprit goes numpy-FREE (CEIL_NUMPY_CARRIER 32 → 31). Carrier-removal #564. The Mat foundation is now complete (Hermitian rc74 + linear-system rc95/rc96 + general-eig rc97), so the matrix-heavy DSP ops can finally flip. esprit (the ESPRIT rotational-invariance DOA/frequency estimator) is the first:
- Its three matrix steps route off the numpy-carrier
matrix_cascadesstack onto the native Mat trio: the Hermitian eigendecomposition →mat_hermitian_eigendecompose, the signal-subspace least-squaresPhi→mat_lstsq, and the rotation-extraction eigenvalues →mat_eigvals. The signal-subspace column-select / shifted-subarray row-slices become plainMat.from_rowsovermat[i, j];np.argsortbecomes a pure-Pythonsorted(range, key=…, reverse=True). The top-levelimport numpy as npis gone, so esprit runs with numpy genuinely absent (a runnable flip riding the native foundation, not the load-only rc70 trap).opnow returns alist[complex](was an ndarray). - Differential-verified value-faithful to the numpy-present reference: clean single-tone DOA recovery within 1e-3, and the trace/determinant invariants of the recovered rotation spectrum match. The two
eigs.shape == (1,)smoke tests move tolen(eigs) == 1; esprit graduates out of the rc63 "cascade-routed" check (it's now numpy-free, a strictly stronger guarantee).
CEIL_NUMPY_CARRIER 32 → 31 (down-only ratchet). No new public op (describe()["tools"]["total"] stays 289, classes 2); ABI 3; no C change. Version bumped at all 5 SSOT locations incl. the scaffolding pin.
[0.7.5rc97] - 2026-06-11¶
Mat-return foundation #3 — mat_eigvals, numpy-free general (non-Hermitian) eigenvalues over the Mat carrier. Carrier-removal #564. mat_hermitian_eigendecompose (rc74) + mat_solve (rc95) + mat_lstsq (rc96) cover the Hermitian + linear-system cases; this closes the general non-Hermitian eigenproblem so esprit's general-eig route can flip off the numpy-carrier matrix_cascades stack. New public amsc.laplacian.mat_eigvals(a: Mat) -> list[complex]:
- Computes the eigenvalue multiset of a general square
Matvia a Wilkinson-shifted QR iteration — Class K (iterate-to-convergence) ∘ Class L (the spectral content) ∘ Householder QR ∘ Class C (the complex Wilkinson shift) — running in plaincomplexlists with theRQrecombination routed through the nativemat_matmul, so it is unconditionally numpy-free. Small sizes take a closed form (n=1scalar; trailing-2×2block via the quadratic over the Class-N_complex_sqrt). The iteration is complex throughout, so it converges to the complex eigenvalues of a real matrix directly (e.g. a rotation block yields±i). - Differential-verified value-faithful to NumPy
eigvalsto machine eps (~5e-14, multiset / nearest-neighbour distance — eigenvalues are unique only as a set) across real-symmetric, real non-symmetric with complex-conjugate pairs, complex, upper-triangular and defective (Jordan-block) inputs. For a HermitianAprefermat_hermitian_eigendecompose(exact Jacobi).mat_eigvalsmatches the shipped numpy-carriermatrix_cascades.eigvalsbit-for-bit (same Wilkinson-shifted-QR).
New public op → describe()["tools"]["total"] 288 → 289 (classes stays 2); ToolEntry + rosetta composition_of_c bucket + __all__/LAPLACIAN_OPS added; CEIL_NUMPY_CARRIER unchanged at 32; ABI 3; no C change. Next: route esprit through the three mat_* ops (hermitian_eigendecompose→mat_hermitian_eigendecompose, lstsq→mat_lstsq, eigvals→mat_eigvals).
[0.7.5rc96] - 2026-06-11¶
Mat-return foundation #2 — mat_lstsq, numpy-free least-squares over the Mat carrier. Carrier-removal #564, the second foundation op unblocking the matrix-heavy DSP carrier-flips. New public amsc.laplacian.mat_lstsq(a: Mat, b: Mat) -> Mat:
- Solves the overdetermined / square (
m ≥ n, full column rank) least-squaresA·X ≈ Bas the normal equationsX = (Aᴴ·A)⁻¹·Aᴴ·B, composed entirely from the nativemat_*trio:mat_solve(mat_matmul(Aᴴ, A), mat_matmul(Aᴴ, B))withAᴴ = A.conj().T. Fully numpy-free for real and complexA(rc95 mademat_solvecomplex-capable). The result is complex iffAis. - Differential-verified value-faithful to
numpy.linalg.lstsqto ~1e-13 (real + complex) for well-conditionedA;m < nraisesValueError. The normal equations square the condition number — fine for the orthonormal signal-subspace projections esprit/the matrix-heavy DSP ops feed it. - Also corrects the
mat_solveToolEntry summary (it still said "Real-f64 only" after the rc95 complex support).
New public op → describe()["tools"]["total"] 287 → 288 (classes stays 2); ToolEntry + rosetta composition_of_c bucket + __all__/LAPLACIAN_OPS added; CEIL_NUMPY_CARRIER unchanged at 32; ABI 3; no C change. Next: mat_eigvals (Mat-carrier shifted-QR), then route esprit through the three mat_* ops.
[0.7.5rc95] - 2026-06-11¶
Mat-return foundation — mat_solve now handles COMPLEX (numpy-free), unblocking the matrix-heavy DSP carrier-flips. Carrier-removal #564. The matrix-heavy signal_processing ops (esprit, dct, fsk, ica_jade, lmmse, map_ml, mimo_svd, music, psk_qam, vector_quantisation) can't be flipped numpy-free as leaves because they receive numpy arrays from hermitian_eigendecompose / matrix_cascades.lstsq / matrix_cascades.eigvals (all numpy-carrier-internal). The genuinely numpy-free path is the native mat_* bridge (rc72–74). This rc closes the first gap:
amsc.laplacian.mat_solvepreviously raisedNotImplementedErroron a complexMat. It now routes complex inputs through the new private_mat_solve_complex, which builds the real 2n×2n block embedding[[Aᵣ,−Aᵢ],[Aᵢ,Aᵣ]]·[u;v] = [bᵣ;bᵢ]from plainMatindexing (no numpy) and rides the shipped native realmat_solve— soX = u + ivis computed with numpy genuinely absent. This is theMat-carrier peer of the existing numpy-carrier_dense_solve_complex.- Differential-verified value-faithful to ~1e-16 (machine eps) vs
numpy.linalg.solvefor well-conditionedA(the signal-subspace projections the DSP ops feed it). The rc73test_mat_solve_complex_rejectedflips totest_mat_solve_complex_via_block_embedding(asserts the solve + residual).
No new public op (describe()["tools"]["total"] stays 287, classes 2; mat_solve was already a public op — this enhances it); CEIL_NUMPY_CARRIER unchanged at 32 (foundation-op enhancement, not a carrier flip); ABI 3; no C change. Next: mat_lstsq (normal-equations via mat_solve∘mat_matmul) + mat_eigvals (Mat-carrier shifted-QR), then route esprit.
[0.7.5rc94] - 2026-06-11¶
Carrier-flip batch #18 — path_b_ops/sign_quantise goes numpy-FREE (CEIL_NUMPY_CARRIER 33 → 32). Carrier-removal #564. The Path-B sign-quantise (Class K pin-slot threshold ∘ Class M dispatch tag; Spike #174 sign-quantise BER anchor) drops its top-level import numpy:
- It was a pure carrier —
np.asarray/np.zeros_like/np.wherewith no helper dependencies, and thenp.whereis the Class-K decision. The flip coerces the signal to alistoffloatand makes the decision an explicit per-element sign-branch (noabs()):v >= threshold → +1 else -1, with the optional dead-band as a three-level+1/-1/0branch. opnow returns alistofint{-1, 0, +1}(was anint8ndarray). The Spike #174path_b_mvpBER test already wraps the dispatch result innp.asarray(..., dtype=np.int8)before.tobytes()/.astype(), so it is robust to the list return.
Differential-verified BIT-EXACT (a pure sign decision; identical {-1,0,+1} to the old np.where). Full local suite green.
CEIL_NUMPY_CARRIER 33 → 32 (down-only ratchet). No new public op (describe()["tools"]["total"] stays 287, classes 2); ABI 3; no C change. Version bumped at all 5 SSOT locations incl. the scaffolding pin.
[0.7.5rc93] - 2026-06-11¶
Carrier-flip batch #17 — sinc_interp goes numpy-FREE (CEIL_NUMPY_CARRIER 34 → 33). Carrier-removal #564. closed_form_ops/sinc_interp (Class L band-limit eigenbasis ∘ Class K pin-slot threshold; Whittaker-Shannon, Oppenheim & Schafer §4.1) drops its top-level import numpy:
- The sinc kernel reuses the rc92 substrate-native
_sinc(x) = sin(πx)/(πx)(rational.sinover the Class-N_PIcascade; thex = 0removable singularity is a Class-K branch returning1.0, no division, noabs()) plus a pure-Python_medianof the consecutive sample-spacing differences (wasnp.median(np.diff(...))). - The complex Whittaker-Shannon matvec
out[q] = Σ_s sinc((t_q−t_s)/T)·y[s]is an inline nested sum over plainlists — notdense_matvec_complex(which is numpy-carrier internally, the rc70 "runnable ≠ loadable" trap; it was also a matmul-ledger site, but the math ratchet assertstotal <= ceil, so removing it stays green). opnow returns alistofcomplex(or a singlecomplexfor a scalartarget_indices), preserving the old scalar-vs-array return shape.
Differential-verified value-faithful to maxerr 1.1e-16 (the rational sinc matches libm to ≤1 ULP; the integer-grid + scalar-target paths are bit-exact 0.0) — well within the op's tolerance. The baseline smoke moves .shape → isinstance/len.
CEIL_NUMPY_CARRIER 34 → 33 (down-only ratchet). No new public op (describe()["tools"]["total"] stays 287, classes 2); ABI 3; no C change. Version bumped at all 5 SSOT locations incl. the scaffolding pin.
[0.7.5rc92] - 2026-06-11¶
Carrier-flip batch #16 — multirate goes numpy-FREE (CEIL_NUMPY_CARRIER 35 → 34). Carrier-removal #564. closed_form_ops/multirate (Class N rational rate-conversion up/down ∘ Class C cyclic streaming; Vaidyanathan 1993 §4) drops its top-level import numpy:
- The default windowed-sinc low-pass taps are built with a substrate-native
_sinc(x) = sin(πx)/(πx)(rational.sinover the Class-N_PIcascade) and a numpy-free_ccosHamming window. Thex = 0removable singularity of_sincis a Class-K branch (returns1.0, no division, never anabs()), matchingnp.sinc(0) = 1. np.arange→range/list-comp;np.zeros→[0.0]*n;np.pi→_PI;np.sum→ builtinsum; the up-sample zero-insert is a plain index loop;_dsp.convolvealready returns alist(rc79); the down-sample is a[::down]slice scaled byup.opnow returns alistoffloat(was an ndarray).- Differential-verified value-faithful to maxerr 7.1e-15 (the rational
sinc/coscascades match libm to ≤1 ULP; theup==down==1identity path is bit-exact 0.0) — well within the op's tolerance. The rc33_ccosHamming test moves off ndarray-broadcast to a list-comp (isinstance/len); the baseline smoke moves.ndim→isinstance(list).
CEIL_NUMPY_CARRIER 35 → 34 (down-only ratchet). No new public op (describe()["tools"]["total"] stays 287, classes 2); ABI 3; no C change. Version bumped at all 5 SSOT locations incl. the scaffolding pin.
[0.7.5rc91] - 2026-06-11¶
Carrier-flip batch #15 — multitaper goes numpy-FREE (CEIL_NUMPY_CARRIER 36 → 35). Carrier-removal #564. closed_form_ops/multitaper (Class L DPSS eigenbasis ∘ Class M tapered-periodogram bundle-average; Thomson 1982) drops its top-level import numpy:
scipy.signal.windows.dpssis an external accelerator (it needs numpy), so it stays a lazy import inside thetry. numpy-absent it raisesImportErrorand the op falls through to the fully numpy-free cosine-taper fallback — whose tapers use the Class-N π cascade (_PI) fed torational.sinvia_csin, ℓ²-normalised by the inlinerational.sqrt(Σvᵢ²)(the olddense_normhelper is numpy-carrier internally, so it cannot be called numpy-free).- The per-taper periodogram
|F|² = real²+imag²(noabs()) and the bundle average are explicit elementwise list comprehensions;_sc.fftreturnsList[complex].opnow returns alistoffloat(was an ndarray). - Differential-verified bit-exact (maxerr 0.0) on the scipy-dpss path (the primary path locally); the fallback path runs numpy-free returning non-negative floats. The rc60
np.linalg.norm-absence ratchet + rc61 routed assertion stay green; the 3 op-smoke.shapeasserts (rc33 ×2 + baseline) move toisinstance/len.
CEIL_NUMPY_CARRIER 36 → 35 (down-only ratchet). No new public op (describe()["tools"]["total"] stays 287, classes 2); ABI 3; no C change. Version bumped at all 5 SSOT locations incl. the scaffolding pin.
[0.7.5rc90] - 2026-06-11¶
Carrier-flip batch #14 — spectral_subtraction goes numpy-FREE (CEIL_NUMPY_CARRIER 37 → 36). Carrier-removal #564 — the first flip whose numpy-free output is not bit-exact-0.0 (it is value-faithful to machine eps). closed_form_ops/spectral_subtraction (Class L FFT-domain PSD ∘ Class N rational floor; Boll 1979) drops its top-level import numpy:
- The signal/noise coerce to
lists offloat; thenp.maximum(|X|²−αN, βN)Class-N floor becomes the builtinmaxper bin;|z|² = real²+imag²(noabs()). np.angle(X)(libmatan2) routes throughrational.atan2(bit-exact to libm here). The phasor + magnitude — previously the numpy-CARRIER helperselementwise_transcendental(phase, "exp_i")+elementwise_sqrt(both usenp.zeros/reshapeinternally — the rc70 "runnable ≠ loadable" trap) — are inlined per-bin asrational.{sqrt,cos,sin}, so the op runs with numpy absent._sc.fft/_sc.ifftalready returnList[complex].opnow returns alistoffloat(was an ndarray). Differential-verified value-faithful to maxerr 6.7e-16 (the rational cascades match libmatan2/cos/sinandsqrtto ≤1 ULP) — well within the op's1e-9tolerance. The baseline smoke moves.shape→len.
CEIL_NUMPY_CARRIER 37 → 36 (down-only ratchet). No new public op (describe()["tools"]["total"] stays 287, classes 2); ABI 3; no C change. Version bumped at all 5 SSOT locations incl. the scaffolding pin.
[0.7.5rc89] - 2026-06-11¶
Carrier-flip batch #13 — stft (+ its spectrogram consumer) go numpy-FREE (CEIL_NUMPY_CARRIER 38 → 37). Carrier-removal #564, the seventh of the workflow-scoped batch and the second windowed follower of cross_spectral — reusing the rc88-codified _PI = float(pi_cascade_digits(30)) (Class-N Archimedes hexagon-doubling) + _ccos → rational.cos π source. closed_form_ops/stft (Class C ∘ A ∘ I ∘ K windowed-frame FFT) drops its top-level import numpy:
- The signal coerces to a
listofcomplex(ahasattr(seq[0], "__len__")guard preserves the old 1-DValueError); the default Hann window0.5·(1 − cos(2π·n/(N−1)))is the same_PI-formed_ccoscascade; per-framesignal·window+_sc.fftbuild a list-of-lists STFT matrix (nonp.zerosstack). opnow returns alistof per-framelists (was a complex128 ndarray).spectrogram(already top-level-numpy-free since rc70) consumes the list-of-lists and computes|z|² = real²+imag²(noabs()) elementwise, returning alistoflists offloat.- The rc33 window/op trig-routing tests + the baseline
stft/spectrogramsmoke tests move.ndim/.shape→isinstance/len; differential-verified bit-exact (maxerr 0.0) against the pre-change numpy path.
CEIL_NUMPY_CARRIER 38 → 37 (down-only ratchet; only stft carried a top-level import, so the count drops by one). No new public op (describe()["tools"]["total"] stays 287, classes 2); ABI 3; no C change. Version bumped at all 5 SSOT locations incl. the scaffolding pin.
[0.7.5rc88] - 2026-06-10¶
Carrier-flip batch #12 — cross_spectral goes numpy-FREE (CEIL_NUMPY_CARRIER 39 → 38). Carrier-removal #564, the sixth of the workflow-scoped batch — and the first sigproc carrier-flip to need π (a moderate flip). closed_form_ops/cross_spectral (Class M HDC bundle-average ∘ Class A FFT cross-product; Welch's method) drops its top-level import numpy:
- The Hann window
0.5·(1 − cos(2π·n/(N−1)))uses a module-level_PI = float(pi_cascade_digits(30))(Class-N Archimedes hexagon-doubling cascade — already the numpy-free π source inexact_dft/spectral_cascades) fed torational.cos. - The cross-product
X·conj(Y), the per-bin power|z|² = real²+imag²(noabs()), and thenp.maximum(..., 1e-30)coherence floor (builtinmax) become explicit elementwise list comprehensions._sc.fftreturnsList[complex];_fc.fftfreqreturns a plain list numpy-absent. opnow returns(list, list)(was two ndarrays). The baseline smoke moves.shape→len; the rc61 coherence test already wrapscohinnp.asarray(resilient).
CEIL_NUMPY_CARRIER 39 → 38 (down-only ratchet). No new public op (describe()["tools"]["total"] stays 287, classes 2); ABI 3; no C change. Version bumped at all 5 SSOT locations incl. the scaffolding pin.
[0.7.5rc87] - 2026-06-10¶
Carrier-flip batch #11 — wiener goes numpy-FREE (CEIL_NUMPY_CARRIER 40 → 39). Carrier-removal #564, the fifth of the workflow-scoped batch and a clean leaf. closed_form_ops/wiener (Class L power-spectrum Laplacian eigenbasis ∘ Class N rational MMSE gain) drops its top-level import numpy:
_sc.fft/_sc.ifftalready returnList[complex]; thenp.asarray/np.realwraps drop. The per-bin power|X|² = X.real² + X.imag²(noabs()), the Class-N rational gainH_W(k) = S_xx/(S_xx+S_nn), and the IFFT-then-real-part are explicit elementwise list comprehensions.- The two
np.maximum(..., 1e-30)ε-floors become the builtinmax(x, 1e-30)per bin. opnow returns alistoffloat(was float64 ndarray). The baseline + rc61 smoke tests move.shape→len; the rc61 finite/real checks pass on the list.
CEIL_NUMPY_CARRIER 40 → 39 (down-only ratchet). No new public op (describe()["tools"]["total"] stays 287, classes 2); ABI 3; no C change. Version bumped at all 5 SSOT locations incl. the scaffolding pin.
[0.7.5rc86] - 2026-06-10¶
Carrier-flip batch #10 — beamforming_fixed goes numpy-FREE (CEIL_NUMPY_CARRIER 41 → 40). Carrier-removal #564, the fourth of the workflow-scoped batch and a clean leaf. closed_form_ops/beamforming_fixed (Class L microphone-array combiner ∘ Class N rational delay coefficients; trauma-informed civilian-acoustics scope) drops its top-level import numpy:
array_signalscoerces to alist-of-listofcomplex(np.complex128/np.full/np.zeros/np.int64→complex()/plain lists/int()); ahasattr(row, "__len__")guard preserves the old 2-DValueError.max_delay = int(np.max(d))→ the builtinmax(d)(Class-L reduce, noabs()); the per-mic delay-and-sumout += w[m] * sig[m, delay:delay+out_len]becomes an explicit Class-M scale-and-accumulate index loopout[i] += w[m]*row[delay+i].opnow returns alistofcomplex(was complex128 ndarray; empty result is[]). The smoke test movesy.ndim == 1/y.shape[0]→isinstance(y, list)/len(y).
CEIL_NUMPY_CARRIER 41 → 40 (down-only ratchet). No new public op (describe()["tools"]["total"] stays 287, classes 2); ABI 3; no C change. Version bumped at all 5 SSOT locations incl. the scaffolding pin.
[0.7.5rc85] - 2026-06-10¶
Carrier-flip batch #9 — polyphase goes numpy-FREE (CEIL_NUMPY_CARRIER 42 → 41). Carrier-removal #564, the third of the workflow-scoped batch and a clean leaf (same shape as fir/matched_filter rc80). closed_form_ops/polyphase (Class L subband Laplacian ∘ Class N rational FIR decomposition) drops its top-level import numpy:
- Its only delegate is the already-numpy-free
_dsp.convolve(returnsList[float]); thenp.asarray(...)wrap around it drops. - The strided polyphase split
E_k[n] = h[k+n·L]and the interpolation interleave are native list[::L]slices; the per-component accumulate (out[:n] += filtered) and the strided interleave write (out[k::L][:m] = c) become explicit Class-M elementwise index loops.np.zeros/np.concatenate/np.array([])→ plain lists. decomposenow returns alistoflist(was list-of-ndarray);opreturns alist(was ndarray). The smoke test movesy.ndim == 1→isinstance(y, list).
CEIL_NUMPY_CARRIER 42 → 41 (down-only ratchet). No new public op (describe()["tools"]["total"] stays 287, classes 2); ABI 3; no C change. Version bumped at all 5 SSOT locations incl. the scaffolding pin.
[0.7.5rc84] - 2026-06-10¶
Carrier-flip batch #8 — ofdm goes numpy-FREE (CEIL_NUMPY_CARRIER 43 → 42). Carrier-removal #564, the second of the workflow-scoped batch. closed_form_ops/ofdm (Class I IFFT ∘ Class L per-subcarrier equaliser ∘ Class K cyclic-prefix) drops its top-level import numpy:
- The modulate path returns a 1-D
list: thenp.zeros(...)baseband buffer becomes[complex(0)] * N, the cyclic-prefixnp.concatenate([prefix, time_block])becomes aprefix + time_blocklist concat, and the per-OFDM-symbol write becomes a list slice-assign._sc.ifftalready returnsList[complex]. - The demodulate path returns a list-of-lists (one row per OFDM symbol). The Class-L one-tap equaliser inlines the numpy-bound
laplacian.elementwise_hypotvia the numpy-free Class-Nrational.hypotin a per-subcarrier comprehension, and thenp.where(|H_k| > 1e-12, H_k, 1.0)guard becomes an explicit Class-K pin-slot sign-branch (> 1e-12 → H_k else 1.0; noabs())._sc.fftalready returnsList[complex]. - Smoke tests move
.shape/.reshape→len/list-flatten at the test boundary (baselinetest_ofdm_smoke+ rc61-routingtest_ofdm_round_trip_value_faithful). The rc61np.fft.residual-callsite assertion still passes (ofdm uses_sc.fft/_sc.ifft).
CEIL_NUMPY_CARRIER 43 → 42 (down-only ratchet). No new public op (describe()["tools"]["total"] stays 287, classes 2); ABI 3; no C change. Version bumped at all 5 SSOT locations incl. the scaffolding pin.
[0.7.5rc83] - 2026-06-10¶
Carrier-flip batch #7 — viterbi goes numpy-FREE (CEIL_NUMPY_CARRIER 44 → 43). Carrier-removal #564, the first of the workflow-scoped batch (a carrier-flip-scoping multi-agent run classified the remaining signal_processing carrier files: 8 flippable-now, 16 blocked behind dense_*/np.linalg/np.fft). closed_form_ops/viterbi (Class L trellis-graph Laplacian ∘ Class K argmax pin-slot) drops its top-level import numpy:
- The 2-D trellis tables
delta/psibecome list-of-lists ([[float('-inf')] * n_states for _ in range(T)]); the branch metricsdelta[t-1] + A[:, s]become an explicit Class-M multiply-add list comprehension over the transition column; and the twonp.argmaxmerge points are the Class-K pin-slot as Pythonmax(range(n_states), key=lambda i: scores[i])— first-maximal tie-break, matchingnp.argmax. Self-contained: no scipy, no helper delegation. viterbi.opnow returns alistofint(was an int64 ndarray). The smoke test movespath.shape == (5,)→len(path) == 5. Value-faithful (same DP, same tie-break).
CEIL_NUMPY_CARRIER 44 → 43 (down-only ratchet). No new public op (describe()["tools"]["total"] stays 287, classes 2); ABI 3; no C change. Version bumped at all 5 SSOT locations incl. the scaffolding pin.
[0.7.5rc82] - 2026-06-10¶
Carrier-flip batch #6 — iir goes numpy-FREE (CEIL_NUMPY_CARRIER 45 → 44). Carrier-removal #564, the same shape as the rc77 allpass flip. closed_form_ops/iir (Class N rational b/a ∘ Class C recursive biquad cascade) drops its top-level import numpy:
- The optional scipy accelerator stays lazy (
scipy.signal.lfilter/sosfilt); scipy needs numpy, so a numpy-absent install falls through to the pure-Python path. The scipy branch now passes thelistinputs straight tolfilter/sosfilt(scipy coerces) and wraps the result inlist(...)so the return type matches the fallback. - The no-scipy path is the direct-form-II transposed difference equation (
_lfilter_direct) — the Class-C recursive cascade of the Class-Nb/arational — now running on plain Pythonlists (explicit multiply-add accumulation ofy[i]and the statez; coefficient normalisation bya[0]as a list comprehension). The biquad-cascade branch chains sections on lists. iir.opnow returns alist(was ndarray). The smoke test moves.shape == (16,)→len(...) == 16. Value-faithful (same difference equation).
CEIL_NUMPY_CARRIER 45 → 44 (down-only ratchet). No new public op (describe()["tools"]["total"] stays 287, classes 2); ABI 3; no C change. Version bumped at all 5 SSOT locations incl. the scaffolding pin.
[0.7.5rc81] - 2026-06-10¶
Carrier-flip batch #5 — wavelet (Haar DWT) goes numpy-FREE (CEIL_NUMPY_CARRIER 46 → 45). Carrier-removal #564. closed_form_ops/wavelet (Class L multi-scale 2-point Laplacian ∘ Class N dyadic 2^k scaling) drops its top-level import numpy:
- Its only numpy was
np.asarray/np.zeroscarriers — the1/√2normaliser was already the libm-free Class-Nrational.sqrt. The op now runs on a plain Pythonlist: each level is an explicit elementwise Class-L 2-point band (approx = [(e+o)/√2],detail = [(e-o)/√2]overzip(evens, odds)) on the Class-N dyadic decimation (current[0::2]/current[1::2]), with a one-zero pad for odd lengths. Value-faithful (samerational.sqrtconstant, same float64 arithmetic). - Returns
(approx, [detail_level_k, …])aslists (was ndarrays). Zero test ripple — the smoke test already assertsisinstance(details, list)+len(details) == 2, both unchanged.
CEIL_NUMPY_CARRIER 46 → 45 (down-only ratchet). No new public op (describe()["tools"]["total"] stays 287, classes 2); ABI 3; no C change. Version bumped at all 5 SSOT locations incl. the scaffolding pin.
[0.7.5rc80] - 2026-06-10¶
Carrier-flip batch #4 — fir + matched_filter go numpy-FREE (CEIL_NUMPY_CARRIER 48 → 46). Carrier-removal #564, the follow-on rc79 unblocked: now that _dsp_cascades is numpy-free, the two convolution/correlation leaf ops flip trivially. Both closed_form_ops/fir (Class N ∘ Class C) and closed_form_ops/matched_filter (Class A ∘ Class C ∘ Class M) drop their top-level import numpy:
- Each had only one remaining numpy use — the
np.asarray(...)return-wrap added at rc79 plus thenp.asarray+.ndiminput guard. Both now delegate straight to the numpy-free_dsp.convolve/_dsp.correlate, which coerce both inputs to 1-D lists (raisingValueErroron a nested/2-D or empty input — same exception type as the prior.ndimguard) and return alist. No numpy anywhere in the op. fir.op/matched_filter.opnow return alist. The two smoke tests move.shape == (N,)→len(...) == N. Value-faithful (same_dspcascade).
CEIL_NUMPY_CARRIER 48 → 46 (down-only ratchet, two files). No new public op (describe()["tools"]["total"] stays 287, classes 2); ABI 3; no C change. Version bumped at all 5 SSOT locations incl. the scaffolding pin.
[0.7.5rc79] - 2026-06-10¶
Carrier-flip batch #3 — the DSP convolution foundation _dsp_cascades goes numpy-FREE (CEIL_NUMPY_CARRIER 49 → 48). Carrier-removal #564. signal_processing/_dsp_cascades (the internal convolve / correlate Class I ∘ Class M cascade) was already numpy-free as a math engine (rc58) but still used numpy as a carrier (np.ascontiguousarray / np.zeros / np.conj / np.result_type). It now runs with no numpy at all:
- The output buffer is a plain Python
list([0] * N, which promotes int → float → complex exactly as the element arithmetic dictates — so an all-integer convolution stays integer, matching numpy'sresult_type); the Class-I shift is a list slice; the conjugate is the element's own.conjugate()(Pythonint/float/complexall provide it). Accumulation order (i outer, j inner) is unchanged, so floats stay bit-faithful to the prior numpy-carrier path (test_dsp_convolution_cascade_rc58.pystill passes). convolve/correlatenow return alist. The 5 consumer callsites —closed_form_ops/{fir, matched_filter, multirate, polyphase}+path_b_ops/matched_filter— wrap the result innp.asarray(...)at their own (still numpy-importing) boundary, preserving their exact ndarray return/behaviour (e.g.multirate'sfiltered[::down] * upstrided-scale,polyphase's.shapeaccumulate). Zero value change. This unblocksfir+matched_filterto flip in a later rc (their only remaining numpy is thenp.asarraycarrier).
CEIL_NUMPY_CARRIER 49 → 48 (down-only ratchet). No new public op (describe()["tools"]["total"] stays 287, classes 2); ABI 3; no C change. Version bumped at all 5 SSOT locations incl. the scaffolding pin.
[0.7.5rc78] - 2026-06-10¶
Carrier-flip batch #2 — signal_processing farrow goes numpy-FREE (CEIL_NUMPY_CARRIER 50 → 49). Carrier-removal #564, continuing the pure carrier-flip phase. closed_form_ops/farrow (Class N — cubic-Lagrange fractional-delay Farrow structure) drops its top-level import numpy:
- The 4-tap Lagrange sub-filter
_FARROW_LAGRANGE_CUBICbecomes a plain-tuple constant table of exact-rational floats (was annp.array), and each per-output-sampleC[k]·xmixer term becomes an explicit length-4 Class-M micro-reduction (c[0]·x[0] + … + c[3]·x[3], left-to-right). At rc26 this dot was routed ontodense_dot_realto retire a numpy matmul site — butdense_dot_realfeeds numpy carriers (np.ascontiguousarray+np.sum) into the native kernel, so a farrow that called it could not run numpy-absent. Inlining the four-term dot is bit-faithful (same IEEE-754 multiply-adds, same order) and adds no numpy matmul site, so the math ratchet stays floored. - Carriers become plain lists (
padded = [0.0] + sig + [0.0, 0.0];outaccumulates byappend); the op now returnslist[float]. The smoke test moves from.shape == (16,)tolen(...) == 16.CEIL_NUMPY_CARRIER50 → 49 (down-only ratchet). No new public op (describe()["tools"]["total"]stays 287,classes2); ABI 3; no C change. Version bumped at all 5 SSOT locations incl. the scaffolding pin.
[0.7.5rc77] - 2026-06-10¶
Carrier-flip batch #1 — signal_processing allpass + sign_quantise go numpy-FREE (CEIL_NUMPY_CARRIER 52 → 50). Carrier-removal #564, the first of the pure carrier-flip phase (the dissolution track closed at rc76 — hurwitz_matrix was the only float-restatement-of-an-exact-op; a full re-review confirmed zero further dissolutions). The numpy-MATH sweep already cascade-routed these ops; what remained was numpy as a carrier. Both modules drop their top-level import numpy:
closed_form_ops/sign_quantise(Class K) — thenp.wheresign-quantiser becomes an explicit per-element Class-K threshold sign-branch over a plain Python list (noabs()); returnslist[int]of{-1, 0, +1}. Exact integer comparisons — value-identical to the numpy logic.closed_form_ops/allpass(Class N) — carriers become plain lists; the optional scipylfilteraccelerator is lazy (scipy needs numpy, so a numpy-absent install falls through) and its result is coerced to a list; the no-scipy path is the existing direct-form-I difference-equation reference running on lists. Returnslist[float].- The two smoke tests move from
.shape == (N,)tolen(...) == N(the numpy-free list carrier), withsign_quantisepinned to its exact{-1,0,+1}output.CEIL_NUMPY_CARRIER52 → 50 (the down-only ratchet). No new public op (describe()["tools"]["total"]stays 287,classes2); ABI 3; no C change. Version bumped at all 5 SSOT locations incl. the scaffolding pin.
[0.7.5rc76] - 2026-06-10¶
The scalar-export layer — One.to_scalar: matrix/vector → scalar, EXACT by default, opt-in numpy-free float export. Carrier-removal #564, the follow-on to the rc75 Hurwitz TOML-class reframe (so a TOML class can chain matrix-math → scalar output). The user's rule (2026-06-10): return float sometimes, never receive float. One.to_scalar(mode='trace'|'sqnorm'|'component', index=None, *, as_float=False) (plus the bindable module-level srmech.amsc.cascade.to_scalar) is the scalar member of the One's projection family — the scalar peer of to_flat_rational.
- Never receive float. Inputs stay exact integers —
the_one(σ, θ_num, θ_den)is integer-only (floatθraises). There is no float-accepting lift; the exact scalar→matrix lift is alreadythe_oneitself. - Return float sometimes. The default is the exact reduced
(num, den)Class-N rational (the math path).as_float=Truedoes the single terminalnum/dencast to a plain Pythonfloat— NO numpy. Pointedly unlikeOne.to_numpy/One.to_matrix(the numpy[scientific]-tier exports #564 is retiring), this float export needs no numpy at all; one boundary cast ≠ error summation. - Three exact modes (
mode=).trace→Tr G(σ,θ) = 3 + 3σ + 8σ·cos θ(the rotation character, the 0/⅓-plane diagonal ofOne.to_matrix);sqnorm→Σ (num/den)²over the 14 state rationals (sign-free, noabs/sqrt);component→ theindex-th of the 14 exact rationals.traceconsumes the same Class-Nrational.cos_series_truncatethetrigonometry/asymptotic_calculuscatalogs validate — those catalogs are this scalar's target test (no forked trig path; their 59 tests stay green). - New public callable ⟹ rosetta
bignum_reference(the exact-rational oracle tier, outside the debt ceilings) +__all__. It takes a structuredOne(no MCP-JSON coercer) so it is not an MCP tool — tool-schema-coverage exemption, likethe_one/greedy_bipartite_alignment; bindable for TOML classes by dotted path (op = "srmech.amsc.cascade.to_scalar").describe()["tools"]["total"]stays 287,classesstays 2;CEIL_NUMPY_CARRIERstays 52. ABI 3; no C change. Newtest_to_scalar_rc76.py(15 tests). Version bumped at all 5 SSOT locations incl. the scaffolding pin.
[0.7.5rc75] - 2026-06-10¶
The numpy-Rosetta-peer dissolution — qm.hurwitz.hurwitz_matrix deleted; the Hurwitz operator is now a [class] over the EXACT the_one; FIRST carrier-ceiling decrement (CEIL_NUMPY_CARRIER 53 → 52). Carrier-removal #564. rc50 shipped qm.hurwitz.hurwitz_matrix, a numpy 14×14 float builder (np.zeros + cos/sin float-divided) that DUPLICATED srmech.amsc.cascade.One.to_matrix — both float-cast the SAME exact cascade the_one. There was never supposed to be a continuous-float Rosetta-peer: chained float ops SUM rounding error at every operation and are not correct-for-science. The EXACT realisation (the One object's 14 exact (num, den) rationals) is the correct one; the float matrix is a lossy projection, legitimate only as an opt-in export, never as a separately-named math op.
- The Hurwitz operator is now declared the class-from-TOML way (
srmech.dsl.make_class, the genome-update mechanism). Newsrmech/amsc/_research/class_catalog/hurwitz.tomldeclares aHurwitz[class]whose methodgenerateBINDSop = "srmech.amsc.cascade.the_one"— the exact, numpy-free generator.make_class("Hurwitz")().generate(sigma=1, theta_num=1, theta_den=4, terms=24)returns the EXACTOne, bit-for-bit identical (to_flat_rational()) tothe_one(1, 1, 4, terms=24)— zero user Python, no numpy, no error summation. "Same operation, different name" → the TOML-class way ([[project_srmech_class_from_toml_user_targeting_model]]). qm/hurwitz.pyis now numpy-free at the top level (noimport numpy; nocos_series/sin_seriesimport) →CEIL_NUMPY_CARRIER53 → 52, the FIRST carrier-ceiling decrement of the carrier-removal arc (rc69–rc74 built the Mat bridge; rc75 flips the first module). What survives inqm.hurwitzishurwitz_planes— the GENUINE cross-derivation of the oriented Fano planes fromoctonion_mult_table(an exact integer-tuple structure, not a float restatement; matchesOne.FANO_PLANESbit-for-bit). Its full numpy-free reachability follows whenqm.octonionflips (the octonion table is still numpy until then).- New
[class]⟹describe()["classes"]["total"]1 → 2 (HurwitzjoinsGenome). The deletedhurwitz_matrixToolEntry + its rosettacomposition_of_cline are removed ⟹describe()["tools"]["total"]288 → 287 (7 count-tests). The float14×14stays as the opt-in lossyOne.to_matrix(the[scientific]export), never a named op.test_hurwitz_rc50.pyrewritten: pins the class-IS-the-exact-the_one identity, the dissolution (not hasattr(h, "hurwitz_matrix")), and the surviving plane cross-derivation. ABI 3. No C change. Version bumped at all 5 SSOT locations incl. the scaffolding pin.
[0.7.5rc74] - 2026-06-10¶
The Mat bridge primitive #3 (the LAST) — numpy-FREE Hermitian eigendecomposition (mat_hermitian_eigendecompose). Carrier-removal #564, completing the family with rc72's mat_matmul and rc73's mat_solve. laplacian.mat_hermitian_eigendecompose(H: Mat) -> (eigvals, eigvecs) diagonalises H = V·diag(λ)·Vᴴ over the numpy-free 2-D Mat carrier with NO numpy: the real/complex Mat.buffer (interleaved-(re, im) row-major) feeds the native srmech_hermitian_eigendecompose(n, H_il, out_eigvals, out_eigvecs_il) zero-copy (a complex Mat) or interleaved-(re, 0) once (a real Mat); eigvals returns as an (n, 1) real Mat (ascending) and eigvecs as an (n, n) complex unitary Mat (always complex, mirroring hermitian_eigendecompose).
- Unconditionally numpy-free. With no native lib — or
n>MAX_NATIVE_NODES(256), or a native convergence miss — the fallback is srmech's own pure-Python cyclic Jacobi (_jacobi_eig_py, the eigenvector-accumulating sibling of_jacobi_eigvals_py): a real-symmetric input diagonalises directly; a complex-Hermitian input is diagonalised through its real2n×2nsymmetric embedding[[A, -B], [B, A]](H = A + iB), the complex eigenvectors reconstructedvⱼ = topⱼ + i·botⱼand same-eigenvalue Gram–Schmidt re-orthonormalised (pins a unitary basis inside a degenerate eigenspace). Noabs(), no libm (off-diagonal magnitude is a sum of squares; the rotation sign is the explicittau ≥ 0Class-K branch; roots via Class-Nrational.sqrt). - Subprocess-proven (numpy blocked at
sys.meta_pathbefore the first import): the op computes with numpy absent on both the native and the forced-embedding-fallback paths. Correctness is pinned by eigenvalues + reconstruction (H ≈ V·diag(λ)·Vᴴ) + unitarity (Vᴴ·V ≈ I), NOT element-wise parity (an eigenvector is fixed only up to a phase; a degenerate eigenspace's basis is solver-chosen); eigenvalues matchnumpy.linalg.eigvalshto ~1e-13 on both paths. Newtest_mat_hermitian_eig_bridge_rc74.py(8 tests) also pins the eigenvalue equationH·v = λ·v, a degenerate complex spectrum, non-square →ValueError, and the empty case. - New public callable ⟹
ToolEntry+ rosettac_dispatched+__all__/LAPLACIAN_OPS;describe()["tools"]["total"]287 → 288 (7 count-tests). The rc72MatMCP coercer + smoke sample already cover it (no MCP-ratchet change). All maths ratchets at floor;CEIL_NUMPY_CARRIERstays 53 (new numpy-free capability, not yet a module flip). ABI 3. No C change. Version bumped at all 5 SSOT locations incl. the scaffolding pin. The Mat↔native-dense-kernel bridge family (matmul + solve + eig) is now complete — aqm.*module's full numpy surface has Mat-native peers, so the first module flip can finally decrement the carrier ceiling.
[0.7.5rc73] - 2026-06-10¶
The Mat bridge primitive #2 — numpy-FREE dense solve (mat_solve). Carrier-removal #564, the peer of rc72's mat_matmul. laplacian.mat_solve(A: Mat, B: Mat) -> Mat solves A·X = B over the numpy-free 2-D Mat carrier with NO numpy: the real Mat.buffers (row-major float64) feed the native srmech_dense_solve_f64(n, nrhs, A, B, out) zero-copy via (c_double*n²).from_buffer(...) (the C side takes them const, so the Mats are not mutated), and the output array('d') wraps straight back into a Mat.
- Unconditionally numpy-free. With no native lib — or any dim >
MAX_NATIVE_NODES(256), or the native path flagging singular — the fallback is srmech's own exact-rational Gauss–Jordan (_solve_exact, Class-NFractiondivision, numpy-free) coerced to float64.srmech_dense_solve_f64is real-f64 only; a complexMatraisesNotImplementedError(the complex solve is the real2n×2nblock embedding — a later rc). - Subprocess-proven (numpy blocked at
sys.meta_pathbefore the first import):mat_solvecomputes with numpy absent on both the native and the forced-fallback paths; value-faithful tonumpy.linalg.solve(~1e-15) when numpy is present. Newtest_mat_solve_bridge_rc73.py(8 tests) also pins singular →ZeroDivisionError, complex →NotImplementedError, and theA·X = A → X = Iround-trip. - New public callable ⟹
ToolEntry+ rosettac_dispatched+__all__/LAPLACIAN_OPS;describe()["tools"]["total"]286 → 287 (7 count-tests). The rc72MatMCP coercer + smoke sample already cover it (no MCP-ratchet change). All maths ratchets at floor;CEIL_NUMPY_CARRIERstays 53 (new numpy-free capability, not yet a module flip). ABI 3. No C change. Version bumped at all 5 SSOT locations incl. the scaffolding pin. rc74 =mat_hermitian_eigendecomposecompletes the bridge family; then aqm.*module's full numpy surface (matmul+solve+eig) has Mat-native peers and the first module flip can decrement the carrier ceiling.
[0.7.5rc72] - 2026-06-10¶
The Mat↔native-dense-kernel bridge: numpy-FREE 2-D matmul (mat_matmul). Carrier-removal foundation #2 (#564). rc69 built the numpy-free 2-D Mat carrier (flat array('d'), row-major, interleaved-(re,im) for complex = C99 double _Complex); rc71 made signal_processing import-reachable numpy-free. This rc adds the bridge the 2-D qm.* matmul callsites flip onto: laplacian.mat_matmul(a: Mat, b: Mat) -> Mat computes A·B with NO numpy on the dispatch path.
- Zero-copy into the native kernel. Because
Mat.bufferis already the exact flat row-major interleaved-complex layout the C symbol reads, a complex operand feedssrmech_dense_matmul_complexzero-copy via(c_double*2·n).from_buffer(mat.buffer)(the C side treats itconst); a real operand is interleaved(re, 0)once. The output is a fresharray('d')wrapped back into aMat(complex iff either input is). This is the bridge half the rc69Matdesign was built for — and it follows the rc#563 numpy-free ctypes-marshalling precedent (thesrmech_jacobi_eigvalslist-marshal), so HAS_NATIVE is True with numpy genuinely absent. - Unconditionally numpy-free. With no native lib — or any dim >
MAX_NATIVE_NODES(256) — the fallback is a pure-Python triple loop over theMat(a cascade, never numpy@). Somat_matmulis numpy-free on BOTH paths. - Honest, subprocess-proven.
test_mat_matmul_bridge_rc72.py(9 tests) pins value-correctness (the native complex kernel is bit-exact vs numpy, max-err 0.0) AND — in a subprocess that blocks numpy atsys.meta_pathbefore the first import — thatmat_matmulcomputes with numpy absent on the native path, the forced-fallback path, and the real path. - New public callable ⟹ full registry treatment:
ToolEntry+ rosettac_dispatchedbucket +__all__/LAPLACIAN_OPS;describe()["tools"]["total"]285 → 286. MCP-invocable too — aMatcoercer (srmech.mcp._coercion, JSON list-of-rows → realMat, mirroring_to_ndarray) so the every-tool-invocable ratchet stays green. All three maths ratchets at floor;CEIL_NUMPY_CARRIERstays 53 (the bridge is a new numpy-free capability, not yet a module flip). ABI 3. rc73+ flips theqm.*matmul callsites ontomat_matmul, lowering the carrier ceiling;mat_solve/mat_hermitian_eigendecomposeare the follow-on bridge rcs.
[0.7.5rc71] - 2026-06-10¶
Lazy op-registration — srmech.signal_processing is now IMPORT-reachable numpy-FREE. rc70 flipped the FFT op-family's runtime math numpy-free, but a fresh import srmech.signal_processing on a numpy-absent install still raised: two uncounted layers above the carrier ratchet. (1) An eager _require_numpy(...) package gate in signal_processing/__init__ (the rc47 capstone), and (2) eager all-op registration imports — from . import path_b_ops → every op module imported for registry population → the numpy ops' import numpy fired transitively, even for the numpy-free FFT family. rc71 dissolves both, so the maths-engine-floored FFT family is finally reachable, not merely runnable, without numpy.
- Op-registration is now LAZY.
closed_form_ops/path_b_opsreplace their eagerfrom . import (…every op…)with a PEP-562 module__getattr__(sharedsrmech._scientific.make_lazy_op_getattr) that defers each op-module import to first attribute access. The numpy-free Path-B ops (the FFT family —fft/ifft/rfft/hdc_truncation/pi_cascade) still import + self-register eagerly (numpy-free); the numpy Path-B ops (matched_filter/sign_quantise/wiener) register a deferred loader with thepath_registryinstead, solookup/has_path/dispatchresolve them by importing-on-demand. A numpyModuleNotFoundErroris re-raised as the clean[scientific]hint at one chokepoint. path_registry.registered_ops()is now declarative — it lists the lazily-registrable numpy ops as pending (no numpy-pulling import forced) after the eagerly-loaded ops;lookup/has_pathgained an_ensure_loaded()that runs the loader on first touch. The eager__init___require_numpy("srmech.signal_processing")gate is removed.- Honest, subprocess-proven: new
test_signal_processing_numpy_free_reachable_rc71.pyspawns a subprocess that blocks numpy atsys.meta_pathbefore the first import (a real numpy-absent install, not in-processmonkeypatch— which can't prove fresh-import-numpy-freedom because the module is already bound), then assertsimport srmech.signal_processingsucceeds,closed_form_ops.fft.op([1,2,3,4])+dispatch("fft", …, path="A")return a numpy-free list,closed_form_ops.music(a numpy op) raises the clean[scientific]hint, andregistered_ops()listsmatched_filter/sign_quantise/wienerdeclaratively. Plus a down-only static guard that the eager_require_numpy()gate stays gone. - No carrier flip ⟹
CEIL_NUMPY_CARRIERstays 53 (rc71 is a reachability fix, not a module flip — it removes the import-time numpy barrier above the already-flipped FFT family). No public-op change ⟹describe()["tools"]["total"]stays 285; all maths ratchets at floor. ABI 3. The 2-Dqm/*matrix modules still need theMat↔native-dense-kernel bridge (next foundation).
[0.7.5rc70] - 2026-06-10¶
Carrier-removal flip #1 — the FFT op-family runs 1-D numpy-FREE. rc69 built the carrier ratchet + the Mat carrier; rc70 is the first real module flip, lowering CEIL_NUMPY_CARRIER 61 → 53. _fft_carrier's numpy was pure carrier-shaping (asarray / moveaxis / reshape / zero-pad / output-alloc) while the transform already rode the numpy-free 1-D spectral_cascades. Now the common 1-D / default-axis path is numpy-free (a stdlib list flows straight through the cascade and comes back a list — the framework-native carrier when numpy is absent, an ndarray for parity when present); only the n-D / non-default-axis case still uses numpy as a carrier, and it imports numpy lazily.
- 8 modules drop their top-level
import numpy(loadable AND 1-D-runnable numpy-free, not merely loadable): the leaf_fft_carrier+ the pass-through wrappersclosed_form_ops.{fft,ifft,rfft}(their redundantnp.asarrayremoved —_fcalready coerces) +closed_form_ops.spectrogram(its only numpy was an annotation-only import) +path_b_ops.{fft,ifft,rfft}(numpy-free length via the input's own.shape/len). - Honest, not theater: a new
test_fft_family_numpy_absent_rc70.pyHIDES numpy (sys.modules["numpy"]=None) and provesfft/ifft/rfft/fftfreq+n=pad/truncate still compute, value-faithful (~1e-9) to the DFT oracle, returning the framework-native list. The values are identical on both paths because both ride the samespectral_cascadescascade — the existing bit-faithful FFT suite (788 tests) stays green. CEIL_NUMPY_CARRIER61 → 53 (down-only carrier ratchet). No public-op change ⟹describe()["tools"]["total"]stays 285; all three maths ratchets unchanged at floor. ABI 3. The 2-Dqm/*matrix modules come in a later rc behind aMat↔native-dense-kernel bridge.
[0.7.5rc69] - 2026-06-10¶
Carrier-removal arc — Phase 0 (infra): the numpy-free 2-D Mat carrier + a down-only carrier ratchet. The numpy-math sweep (rc53–rc68) drove the linalg_fft / matmul / ufunc ledgers to their floor — numpy is gone as a math engine. What remains is numpy as a carrier (array container / shaping): 61 submodules still import numpy at module level, so they can't load on a numpy-free install (the package itself already imports numpy-free; these are lazy submodule imports). This rc lays the foundation to delete that — no module is flipped yet.
- New
srmech.amsc.mat.Mat— the 2-D peer of theHVcarrier (rc29). A dense matrix over a flat stdlibarray('d'): row-major, with interleaved(re, im)for complex — exactly C99double _Complexlayout, soMat.bufferis directlyctypes-castable to the buffers the native dense kernels (dense_matmul/dense_solve/hermitian_eigendecompose) read/write, no copy and no numpy on the HAS_NATIVE path. Imports no numpy at load (the opt-in.to_numpy()bridge is a lazy import; the numpy-free path is.tolist()/.tobytes()/.buffer). Construction (from_rowsauto-detecting complex,from_flat), plain-scalarmat[i, j],row,transpose/T,conj(Class-K imag sign-flip), value-__eq__vsMat/ list-of-rows / numpy 2-D, andto_numpy()(realfloat64/ complexcomplex128). - New down-only carrier ratchet
test_numpy_carrier_ratchet.py— counts modules with a top-levelimport numpyand asserts== CEIL_NUMPY_CARRIER(61). The number only goes DOWN: each module flipped ontoMat/HV+ the native ctypes kernels decrements it; a new hardimport numpyis a regression. Plus a guard thatmat.pyitself stays numpy-free (so it never bumps the very ratchet it exists to drive down). Newtest_mat_carrier_rc69.py(10 tests, incl. the numpy-absent path). - Carrier is an unregistered handle type (like
HV) ⟹ no ToolEntry / introspect / rosetta gates;describe()["tools"]["total"]stays 285. ABI 3. No module flips ⟹ all three maths ratchets unchanged at floor. rc70+ flips modules ontoMat, one cluster per rc, lowering the carrier ceiling toward 0.
[0.7.5rc68] - 2026-06-10¶
Real-antisymmetric eig → iS-Hermitian cascade route (numpy-removal — linalg_fft decrement). Both np.linalg.eig sites in qm/so8.py consume the eigenpairs of a real antisymmetric matrix — the J complex-structure operator (584) and ad(H) for a Cartan element (646), each with a purely-imaginary ±i·weight spectrum. For a real skew S, iS is Hermitian, so the eigenpairs come straight from the already-shipped, C-backed hermitian_eigendecompose(iS): eigenvalues λ_S = −i·μ (μ real, ascending), eigenvectors the same V. A new private so8._eig_real_skew routes both onto that Hermitian cascade — no np.linalg.eig.
- Why it's sound under degeneracy: the eigenvector phase / degenerate-eigenspace basis is solver-chosen (exactly as
np.linalg.eigalready left it), so this is only valid because so8 consumesVinvariantly — 584 rebuilds the triplet from the+ieigenspace span (the projector is basis-invariant, even forJ's degenerate mult-3 eigenspace), 646 reads scale/phase-invariant Rayleigh-quotient weights. Both invariances are differential-tested againstnp.linalg.eigon a degenerate (mult-3) real-skew matrix; the full so8 / triality / an_embedding / quaternion / killing / semisimple suite (80 tests) stays green with both sites routed. - numpy-math ratchet
linalg_fft25 → 23 (2 genuineeigcalls; the helper's 2 docstring mentions arenumpy.linalg.-free). Private helper ⟹ no ToolEntry/registry gate;describe()["tools"]["total"]stays 285. ABI 3. Newtest_real_skew_eig_routing_rc68.py. - Maths-engine sweep floored. The 1 remaining
np.linalg.eig(pseudo_hermitian:182, on a generalOthat is not skew → the iS trick does not apply) needs a general non-Hermitian eigenvector cascade that does not exist; the rest oflinalg_fftis numpy-as-accuracy (matrix_rank+ so8svdsmall-s/nullspace +mimo_svd) or irreducible fallbacks inside the cascade ops. The next numpy removal is the carrier layer.
[0.7.5rc67] - 2026-06-10¶
Real-symmetric eigh → C-backed hermitian_eigendecompose + eigenvector sign-canonicalisation (numpy-removal — linalg_fft decrement). Lane 1 past the rc66 route-safe floor: laplacian.symmetric_eigendecompose's np.linalg.eigh routes onto the already-shipped, C-backed hermitian_eigendecompose (real-symmetric IS complex-Hermitian — native Jacobi peer when present; NumPy eigh only as that op's own shared fallback). The eigenvectors of a real-symmetric matrix are real (the Hermitian path returns them with imaginary part ~0), so the result is taken .real and a new private _canonicalize_eigenvector_signs pins each column's ±1 sign.
- Eigenvector sign canonicalisation (Class K): an eigenvector is defined only up to a
±sign (aZ₂gauge for a real-symmetric problem); LAPACK / the native Jacobi peer pick it arbitrarily — a hidden, non-settable convention. The helper flips each (real) column so its largest-magnitude entry is positive — a deterministic, settable convention (the endianness precedent), and the flip IS the Class-K sign boundary. The magnitude pivot is selected viacol², so there is noabs()and no float square root (the originalre²+im²+√draft tripped the A-N cascadefloat_powratchet — the sign-flip form is both honest-to-Class-K and float-power-free). - Degeneracy: within a degenerate eigenspace the basis (the
U(k)rotation) is still solver-chosen and reconstruction-invariant; sign-canon pins the per-columnU(1)part. Correctness is verified by the basis-INVARIANT properties — ascending eigenvalues (== eigvalsh), reconstructionL = V·diag(w)·Vᵀ, orthonormality — on non-degenerate AND degenerate inputs (4-cycle Laplacian{0,2,2,4}, identity, block-degen), all yielding real orthonormalVon both the native and numpy-fallback paths. - numpy-math ratchet
linalg_fft28 → 25 (1 genuineeighcall + 2 textual docstring mentions removed). Rosetta:symmetric_eigendecomposemovespython_only_debt→composition_of_c(it now composes the c_dispatched hermitian op), soCEIL_PYTHON_ONLY_DEBT108 → 107 (debt closed). Private helper ⟹ no ToolEntry/registry gate;describe()["tools"]["total"]stays 285. ABI 3. Newtest_symmetric_eigh_canon_routing_rc67.py.
[0.7.5rc66] - 2026-06-10¶
Complex inv → real 2n×2n block embedding of the native dense_solve (numpy-removal — linalg_fft decrement). rc65 hit the trivial carrier-swap floor of the linalg_fft sweep; rc66 is its first new-capability step. The lone complex np.linalg.inv site — qm/pseudo_hermitian.py's η = (V·Vᴴ)⁻¹ (the Mostafazadeh η-metric) — routes onto a new private Class-L helper laplacian._dense_solve_complex.
- A complex system
(Aᵣ + i Aᵢ)(u + i v) = (bᵣ + i bᵢ)is, splitting real/imaginary parts, the real2n×2nsystem[[Aᵣ, −Aᵢ], [Aᵢ, Aᵣ]]·[u; v] = [bᵣ; bᵢ](soX = u + i v). The embedding is exact (NumPy a carrier only —concatenate/slice/.real/.imag) and rides the shipped native realdense_solve; for a well-conditionedA(the Gram matrixV·Vᴴis HPD) it is value-faithful to NumPy's complexinv/solveto ~1e-9. The complex inverse is just_dense_solve_complex(A, eye(n)). - Verified
== numpyfor HPD inv +M·M⁻¹=I+ general complexsolve(vector & matrix RHS); the η construction still yields a valid metric (O†η = ηO). 52 pseudo_hermitian tests pass. Private (underscore) helper ⟹ no registry-gate / ToolEntry overhead;describe()["tools"]["total"]stays 285. ABI 3. Newtest_complex_solve_block_embed_rc66.py. - numpy-math ratchet
linalg_fft29 → 28.np.linalg.inv→ 0. - Route-safe floor reached. The remaining 20 genuine
np.linalg.*sites are not carrier-swaps: numpy-as-accuracy (6matrix_rank+ 7 so8svd, all small-singular-value / null-space), irreducible numpy fallbacks inside the cascade ops (dense_solve'ssolve+hermitian_eigendecompose'seigh), or need eigenvector-sign-canonicalization (eig×3 /eigh-1062 /mimo_svdpublic-API convention). The maths-sweep residual is a legitimate mix, not unfinished carrier work.
[0.7.5rc65] - 2026-06-10¶
np.linalg.pinv → cascade SVD reconstruct (numpy-removal — linalg_fft decrement). rc64 noted the cascade SVD is value-faithful for large singular values; rc65 cashes that in on the lone genuine np.linalg.pinv site — the qm/so8.py _killing_form structure-constant least-squares solve. A new _pinv helper reconstructs the Moore-Penrose pseudoinverse from the cascade SVD, A⁺ = V·diag(1/s)·Uᴴ, with NumPy's rcond = 1e-15 small-singular-value cutoff.
- Why it's route-safe (unlike
matrix_rank): the pseudoinverse is unique, so the per-factor U/V column-sign ambiguity of the SVD cancels; and the generator stack_killing_formforms is full column rank (the docstring's semisimplicity certificate), so every singular value sits well clear of the cutoff — no nullspace/small-saccuracy is exercised. Verified_pinv == numpy.linalg.pinv(~1e-7) across the(28, n)shapes the module forms, including the actualpinv·bracketmatvec usage and the definingA·A⁺·A = Aidentity. The reconstruction useslaplacian.dense_matmul_real(the Class-L cascade), not@, so the matmul ledger is untouched. - 23 so8 / Killing-form / quaternion-stabiliser tests pass post-route (incl. the g₂ full-rank-14 Cartan semisimplicity check that the rc63b
matrix_rankroute had broken —_pinvdoes not touchmatrix_rank). 1 textualnumpy.linalg.pinvreworded to "NumPy'spinv". - numpy-math ratchet
linalg_fft30 → 29. No new public op ⟹describe()["tools"]["total"]stays 285. ABI 3. Newtest_pinv_cascade_routing_rc65.py. Still deferred: complexinv(needs a complex solve),eigh/eigvalsh(hermitian_eigendecompose, eigenvector-sign-delicate),eig/svd-direct.matrix_rankstays on numpy permanently (numpy-as-accuracy).
[0.7.5rc64] - 2026-06-10¶
map_ml covariance inverse → dense_solve (numpy-removal — linalg_fft decrement). The 2 real np.linalg.inv covariance-inverse sites in signal_processing/closed_form_ops/map_ml.py (R_noise⁻¹ / R_prior⁻¹ in the ML / MAP estimators) route onto the already-exported Class-L solve laplacian.dense_solve(M, np.eye(n)) — the inverse is the linear solve M·X = I (unique; for well-conditioned covariances bit-faithful to numpy.linalg.inv to ~1e-9).
- Important boundary found + recorded: the
qm/so8.pynp.linalg.matrix_ranksites were investigated and kept on numpy. The cascade SVD is value-faithful for large singular values (reconstruction /Q@Qᵀprojectors /pinv) but its nullspace (small-singular-value) accuracy is too low for an absolute-tol rank count on a rank-deficient matrix — routing them over-countedrank_with_so4(6 → 9). That is a legitimate numpy-as-accuracy site (deferred with thedense_solvenumpy fallback + the matmul-4 kernel-internal tail), not numpy-as-carrier. - numpy-math ratchet
linalg_fft32 → 30. No new public op ⟹describe()["tools"]["total"]stays 285. ABI 3. Newtest_map_ml_inv_dense_solve_rc64.py. Deferred to later sub-rcs:pinv(svd-reconstruct, sign-invariant), complexinv(needs a complex solve),eigh(hermitian_eigendecompose),eig/svd-direct.
[0.7.5rc63] - 2026-06-10¶
The np.linalg.* cluster, first sub-batch (numpy-removal — linalg_fft decrement). With np.fft.* drained (rc62), the final linalg_fft front is the np.linalg.* decomposition cluster. rc63a takes the sites whose downstream use is invariant to the decompositions' inherent ambiguities, routing them onto the already-shipped value-faithful srmech.amsc.cascade.matrix_cascades cascades (qr/eigvals, rc38/rc39).
- 5
q, _ = np.linalg.qr(X)callsites (qm/so8.py) →matrix_cascades.qr. Each consumesQonly throughQ @ Qᵀprojectors / its column span / a sum-of-squares leak test — all invariant to QR's per-column sign, somatrix_cascades.qr(faithful up to column sign) is exact for the usage (verifiedQ @ Qᵀ == numpyto ~1e-9). - 2
np.linalg.eigvals(X)callsites (qm/pseudo_hermitianmax|imag|,signal_processing/.../espritrotation set) →matrix_cascades.eigvals. Both consume the eigenvalue multiset (order-free); the cascade is set-faithful to ~1e-12. - 83 so8/esprit/pseudo_hermitian/triality op-tests pass post-route. Plus ~22 textual
numpy.linalg.Xfaithfulness/summary mentions (inmatrix_cascades/tool_schema/triality— none have genuine calls) reworded to "NumPy X". - numpy-math ratchet
linalg_fft61 → 32. No new public op ⟹describe()["tools"]["total"]stays 285. ABI 3. Newtest_linalg_qr_eigvals_routing_rc63.py. Deferred to rc63b/c: the sign/order-delicatesvd/matrix_rank/inv/eig/eigh/solve/lstsq/pinv(mostlyqm/so8.py+amsc/laplacian.py).
[0.7.5rc62] - 2026-06-10¶
Draining the np.fft.* family (numpy-removal — linalg_fft decrement). rc61 routed the 1-D np.fft.fft/ifft callsites; rc62 finishes the np.fft.* side. New srmech.signal_processing._fft_carrier lifts the 1-D spectral_cascades FFT cascade to NumPy's full ndarray + n= (zero-pad / truncate) + axis= contract — NumPy is a carrier only (moveaxis / reshape / zero-pad / slice), the transform rides the cascade. It adds a real-input rfft (full transform then slice; complex input raises TypeError, mirroring NumPy) and an fftfreq carrier (integer bin indices /(n·d); no transcendentals).
- 8 genuine
np.fft.*callsites routed — the 6 Path-A / Path-B reference opsclosed_form_ops/{fft,ifft,rfft}+path_b_ops/{fft,ifft,rfft}+ the 2cross_spectralfftfreqsites — and the ~18 textualnumpy.fft.Xdoc/summary mentions reworded to "NumPy fft".np.fft.→ 0. - Carriers verified bit-faithful to NumPy across real + complex, 1-D + n-D, every axis, and
npad/truncate (~1e-9).rfftis value-faithful (~1 ULP), NOT bit-identical to NumPy's pocketfft real-FFT — evennp.fft.fft(real)[:n//2+1]≠np.fft.rfftbit-for-bit, so matching pocketfft's exact rounding was a NumPy-backend artifact, not a substrate property. The 4rfft-vs-NumPy assertions move toallclose; the Path-A == Path-B cascade identity stays exact (both ride the same cascade). - numpy-math ratchet
linalg_fft87 → 61. No new public op ⟹describe()["tools"]["total"]stays 285. ABI 3. Newtest_fft_carrier_rc62.py. Nextlinalg_fftfront: thenp.linalg.{svd,qr,eig,solve,inv,...}decomposition cluster (~36 sites, mostlyqm/so8.py).
[0.7.5rc61] - 2026-06-10¶
The np.fft.* family, first batch (numpy-removal — linalg_fft decrement). rc60 opened the linalg_fft ledger on the np.linalg.norm cluster (122 → 102); rc61 takes the np.fft.* side. The 15 one-dimensional np.fft.fft(x) / np.fft.ifft(x) callsites across signal_processing route onto the existing value-faithful srmech.amsc.cascade.spectral_cascades.fft / .ifft cascade (the rc36/rc37 radix-2 Cooley–Tukey FFT with a dft fallback for non-power-of-2 N — exact-until-rotation). No new public op: the FFT cascade already shipped; rc61 only swaps the carrier (NumPy's FFT → the cascade), value-for-value, wrapping np.asarray(...) so the result stays an ndarray.
- 15 callsites routed across
signal_processing/closed_form_ops/{cross_spectral ×4, ofdm ×2, spectral_subtraction ×2, multitaper ×1, stft ×2, wiener ×2}+path_b_ops/wiener ×2— eachnp.fft.fft(x)→np.asarray(_sc.fft(x)),np.fft.ifft(x)→np.asarray(_sc.ifft(x)). The cascade is verified== numpyacross real + complex inputs at power-of-2 AND non-power-of-2 N (2…100, ~1e-9). The DSP-op invariants (Welch coherence, Wiener gain, OFDM modulate↔demodulate round-trip, spectral-subtraction floor, STFT frames, multitaper PSD) are preserved — 447 signal_processing tests pass post-route. - numpy-math ratchet
linalg_fftceiling 102 → 87. No new public op ⟹describe()["tools"]["total"]unchanged (285). ABI 3. Newtest_fft_cascade_routing_rc61.py. The remainingnp.fft.*sites are then=/axis=Path-A reference ops (fft.py/ifft.py/rfft.py+fftfreq), which need an array-aware (n-pad + axis) wrapper over the 1-D cascade — next batch; then thenp.linalg.{svd,qr,eig,solve,inv}decomposition cluster (~36 sites, mostlyqm/so8.py).
[0.7.5rc60] - 2026-06-10¶
The dense_norm cascade (numpy-removal — opening the linalg_fft ledger). With the matmul ledger down to its 4 deferred callsites (rc59), the sweep turns to the linalg_fft ledger (122 — np.linalg.* / np.fft.*). rc60 takes its single biggest cluster: the 20 default np.linalg.norm callsites (the QM self-consistency residuals + the signal-processing taper normalisations), all the Euclidean 2-norm / Frobenius norm √(Σ|xᵢ|²).
- New public op
srmech.amsc.laplacian.dense_norm(x)— Class N (therational.sqrtroot) ∘ Class M (thedense_dot_complexself-bindΣ|xᵢ|²): the array is flattened (a carrier reshape), the sum-of-squares rides the native elementwise-bind cascade, and the root is the libm-free Class-N sqrt. numpy is a carrier only (no norm engine). Value-faithful to the NumPy 2-norm / Frobenius norm (~1 ULP) across every shape and dtype — verified real + complex, 1-D + n-D. - 20 callsites routed across
qm/{so8 ×7, relativistic ×3, spin ×3, triality ×3, gauge, sm, pseudo_hermitian}+signal_processing/closed_form_ops/multitaper. - numpy-math ratchet
linalg_fftceiling 122 → 102. New public op ⟹describe()["tools"]["total"]284 → 285 (+1 ToolEntry,+1rosettacomposition_of_cline,__all__+LAPLACIAN_OPS). ABI 3. Newtest_dense_norm_cascade_rc60.py. Nextlinalg_fftbatches: thenp.fft.*family (→ thespectral_cascadesFFT/DFT cascades) and thenp.linalg.{svd,qr,eig,solve,inv}cluster (→ thematrix_cascades/laplaciandecompositions).
[0.7.5rc59] - 2026-06-10¶
The QR shape-polymorphic pass (numpy-removal matmul decrement). rc58 dropped the matmul ledger to 12 genuine compute callsites; rc59 routes the 8 matrix_cascades QR-internal sites onto the existing value-faithful dense_* kernels in srmech.amsc.laplacian — no new kernel, so the decompositions keep riding the native srmech_dense_matmul_complex path.
- Householder reflectors (
qr): the twonp.vdot(v, v)Hermitian self-binds →dense_dot_complex(np.conj(v), v)(plain bilinear,conjpassed explicitly); the two reflector applicationsnp.outer(v, conj(v)·R)/np.outer(Q·v, conj(v))→dense_outer_complexoverdense_matvec_complex(theconj(v)·RandQ·vmatvecs). - lstsq back-solve (the shape-polymorphic case):
Qᴴ·b→dense_matvec_complex(1-D rhs) /dense_matmul_complex(k-column rhs); the triangularR[i,i+1:]·x[i+1:]→dense_dot_complex(1-Dx) /dense_matvec_complex(k-columnx)._norm2'snp.vdot→dense_dot_complextoo. - The kernels are value-faithful, so the QR/SVD/lstsq/eig INVARIANTS (reconstruction, orthonormal
Q, upper-triangularR, singular-value set, eigenvalue multiset) are preserved — verified vs numpy across real+complex and 1-D + multi-column right-hand sides. Newtest_qr_householder_cascade_routing_rc59.py. - numpy-math ratchet
matmulceiling 12 → 4. The final 4 are genuinely deferred:ica_jade'snp.einsum(the hot JADE cumulant loop) +laplacian's SchurL_pi·X+ thedense_matvec_complexkernel-INTERNAL@fallback (a kernel can't route onto itself).tools.totalstays 284; no public-op / rosetta / ToolEntry change. ABI 3. Next numpy-math front: thelinalg_fftledger (122 —np.linalg.*/np.fft.*).
[0.7.5rc58] - 2026-06-10¶
The convolution / correlation cascade (numpy-removal matmul decrement). With the matmul ledger reword-swept to its 18 genuine compute callsites (rc57), rc58 takes the documented next batch: the six np.convolve / np.correlate DSP sites. A length-N convolution is a rank-1 accumulate — a small matrix product in disguise — so it lives on the matmul ledger; rc58 replaces it with the substrate-native cascade.
- New numpy-free helper
srmech.signal_processing._dsp_cascades.{convolve,correlate}— direct linear convolution as Class I (cyclic shift, the running output offset) ∘ Class M (scaled accumulate,full[i:i+nb] = full[i:i+nb] + a[i]*b), with full/same/valid edge modes. NumPy is a carrier only (np.zerosbuffer, elementwise+/*, slicing) — no convolve/matmul/math-ufunc.correlate=convolve(a, conj(v)[::-1])(np.conjis a carrier, not a counted ufunc) with NumPy's exactsame-mode crop (centre onfloor(diff/2)forlen(a) >= len(v),ceil(diff/2)forlen(a) < len(v)— the historical NumPy swap convention). Value-faithful to NumPy across 15 length-combos × 3 dtypes (real/complex/int) × 3 modes. - Six DSP callsites routed:
fir,multirate,polyphase(×2) offnp.convolve;closed_form_ops.matched_filter+path_b_ops.matched_filteroffnp.correlate. Behaviour-identical (the matched-filter Path A / Path B outputs still agree bit-for-bit on real input). - Not a public
srmech.amsc.*op — it composes carrier arithmetic with no own C symbol, so promoting it tosrmech.amsc.cascadewould only addpython_only_debtdebt (which the down-only Rosetta ratchet forbids without a C twin). A future rc can promote it with a native C twin.tools.totalstays 284; no rosetta/ToolEntry change. - numpy-math ratchet
matmulceiling 18 → 12. The remaining 12 are thematrix_cascadesQR-internals (8),ica_jade'snp.einsumhot loop (2), andlaplacian's Schur /dense_matvec(2). ABI 3. Next matmul work: the QR shape-polymorphic pass; then thelinalg_fftledger (122).
[0.7.5rc57] - 2026-06-10¶
The matmul-ledger reword sweep (numpy-removal — docs only, zero behavior change). With the ufunc bucket closed (rc52–rc56), the numpy-math sweep turns to the matmul ledger. Auditing its 48 matches revealed ~30 are textual @ / np.{vdot,einsum,convolve,correlate,kron} references in docstrings, comments and ToolEntry summaries (plus one genuine false positive — profile_loader's entry-point[…] @ … f-string) — not compute. rc57 strips them all (writing the NumPy op name / · without the dotted-paren form, per the established convention), so the ratchet count reflects only real callsites.
- numpy-math ratchet
matmulceiling 48 → 18. The remaining 18 ARE genuine deferred compute, each tracked for its own future cascade work:matrix_cascadesQR-internals (vdot/outer/@/back-solve — the shape-polymorphic pass),ica_jade'snp.einsum(the hot JADE Jacobi loop),fir/multirate/polyphasenp.convolve+matched_filternp.correlate(distinct-op convolution/correlation cascades),laplacian's SchurL_pi·X+ thedense_matvec_complexkernel-internal@. - Pure documentation/comment/summary rewording — no code-logic change,
tools.totalstays 284, no rosetta/ToolEntry-count change. ABI 3. Next matmul work: the convolve/correlate cascades, then the QR shape-polymorphic pass; then thelinalg_fftledger (122).
[0.7.5rc56] - 2026-06-10¶
elementwise_transcendental is numpy-free — the ufunc bucket CLOSES to zero (numpy-removal). rc52→rc55 routed every external numpy-math ufunc callsite onto srmech cascades; the last 10 were elementwise_transcendental's OWN internal numpy fallback (its complex-input path + its no-native real path). rc56 drives those to zero — numpy is now purely a carrier across the entire transcendental + magnitude surface.
- Real no-native fallback →
rational.{exp,cos,sin,log}loop (_real_transcendental_loop): the Pyodide / pure-Python path now runs the Class-N scalar cascades (bit-exact vs numpy over the tested range; the log domain guardarr > 0is preserved). The native path still dispatchessrmech_elementwise_transcendental(the libm-free C cascade) — unchanged. - Complex-input path → per-element Class-N complex cascades (
_complex_transcendental_loop):exp(z)=rational.complex_exp;cos(z)/sin(z)viacosh/sinhbuilt fromrational.exp(cos(a+bi)=cos a·cosh b − i·sin a·sinh b, etc.);log(z)=rational.log(rational.hypot(a,b)) + i·rational.atan2(b,a)(principal branch; rejectsz=0). Previously this branch was the documented "out-of-scope-for-v1" numpy fallback. - The numpy-math ratchet's
ufuncceiling drops 10 → 0. Across rc52–rc56 the ufunc bucket went 48 → 0: every transcendental / magnitude / sign op in the package —hypot,exp,sqrt,log,sin,cos,sign— now runs a libm-free srmech cascade, with numpy only ever packing the array. Pure routing + numpy-free kernels —tools.totalstays 284; no new public op / rosetta / ToolEntry. ABI 3. Next numpy-math front: thelinalg_fftledger (122 —np.linalg.*/np.fft.*).
[0.7.5rc55] - 2026-06-10¶
The np.log + np.sign external residue → cascade (numpy-removal ufunc decrement). Clears the last genuinely-external ufunc-math callsites (rc52 hypot → rc53 exp → rc54 sqrt → rc55 log/sign). Pure routing — no new public op.
- Scalar
np.log→rational.log(2):mlse's uniform-priorA_logandpi_log(−log(A)/−log(n_states)) now run the Class-N log cascade (no libm). - Array
np.sign→ Class-K comparison sign (1):hdc.polar_bundle's Pyodide / no-native fallback (the C peersrmech_polar_bundlehandles the native path) computes the per-position sticky-majority sign via(total > 0) − (total < 0)— carrier comparisons (the Class-K pin-slot at zero: + sector / 0 boundary / − sector), nonp.signufunc, noabs(), bit-identical tonp.signfor the real int sum. - The numpy-math ratchet's
ufuncceiling drops 15 → 10, plus 2 ratchet-visible comment mentions de-parened. The remaining 10 are ALLelementwise_transcendental's OWN internal numpy fallback (2 exp + 3 sin + 3 cos + 2 log — its complex-input path + its no-native real path); driving those to zero is the deferred cascade-kernel work (needs complex-input trig/exp/log cascades), tracked separately. Pure routing —tools.totalstays 284; no rosetta/ToolEntry change. ABI 3; numpy carriers-only.
[0.7.5rc54] - 2026-06-09¶
laplacian.elementwise_sqrt — the np.sqrt batch (numpy-removal ufunc decrement). Continues the ufunc sweep (rc52 hypot → rc53 exp). The new elementwise_sqrt(arr) computes √arrᵢ per element via the Class-N rational.sqrt cascade (isqrt-based, native srmech_rational_sqrt-dispatched, no libm) — the companion to elementwise_hypot, so numpy carries the array only.
- Array
np.sqrt→elementwise_sqrt(2):spectral_subtractionPSD magnitude√(new_psd),ica_jadewhitening1/√(eigvals). - Scalar
np.sqrt→rational.sqrt(10):qm.gaugeGell-Mann1/√3(×3),qm.potentials√nharmonic-oscillator ladder,qm.relativisticKlein-Gordon on-shell energy,qm.so8(×2),qm.triality,psk_qam√M,wavelet1/√2. - The numpy-math ratchet's
ufuncceiling drops 27 → 15. Round-off-faithful (rational sqrt floor-projected vs IEEE round-to-nearest — a ≤1-ULP shift; bit-exact on perfect squares — the QM ladder/dispersion + DSP whitening/decode suites pass unchanged). 7 modules gain afrom srmech.amsc import rational as _srnimport. - 1 new
srmech.amsc.laplacian.elementwise_sqrtToolEntry →describe()["tools"]["total"]283 → 284;composition_of_crosetta bucket (composes thec_dispatchedrational.sqrt/srmech_rational_sqrt; no own C symbol). Added to__all__+LAPLACIAN_OPS. Class N (rational √) over a Class-L array surface; numpy carriers-only. ABI 3.
[0.7.5rc53] - 2026-06-09¶
The np.exp batch — 14 complex-phase / real-exp callsites routed off numpy's exponential ufunc (numpy-removal). Continues the ufunc decrement opened in rc52. np.exp(1j·x) is the unit-modulus phase e^{iθ} = cos θ + i sin θ; routing it onto elementwise_transcendental(·, "exp_i") runs the real cos/sin through the native libm-free C cascade (srmech_elementwise_transcendental) and assembles the complex result — numpy carries the array only.
- Array
e^{iθ}phases →elementwise_transcendental(·, "exp_i")(9 sites):qm.gaugeSU(N) time-evolutiondiag(e^{iλ}),qm.single_particleTDSE/Heisenberg/Liouville per-mode phasese^{−iλt}(×3),fsktone-bank generation (×2),psk_qamM-PSK constellation,spectral_subtractionphase re-attach,spectralper-mode phase-extrapolation, andlaplacian's magnetic directed-graph phasee^{i·2πq·θ}. - Real-array exp →
elementwise_transcendental(·, "exp")(2 sites):heat_kernelheat-decaye^{−tλ},rbs_lm.inferencesoftmax. - Scalar
e^{iδ}→rational.cexp(2 sites):qm.sm's CKM Dirac CP phase (Euler — Class-N trig ∘ Class-C i-rotation). - The numpy-math ratchet's
ufuncceiling drops 43 → 27. Plus 2 ratchet-visible doc/summarynp.exp(mentions de-parened. Round-off-faithful (the cascade trig is ~1 ULP off libm; never flips a unitarity / argmax / decode outcome — the full QM + DSP suites pass unchanged). The 2 remainingnp.expareelementwise_transcendental's OWN complex-input fallback + real no-native fallback (the documented internal kernel; deferred). Pure routing — no new public op,tools.totalstays 283. ABI 3; numpy carriers-only. Next: thenp.sqrtcluster (12), thennp.sin/np.cos/np.log/np.sign.
[0.7.5rc52] - 2026-06-09¶
laplacian.elementwise_hypot — the |z| magnitude cascade opens the np.hypot ufunc decrement (numpy-removal). With the np.outer contraction surface handed to the cascade (rc51), the sweep turns to the ufunc bucket — numpy's transcendental / magnitude engine. np.hypot(z.real, z.imag) is |z| = √(re² + im²), a per-element libm hypot over the array; elementwise_hypot(a, b) computes the same magnitude by looping the Class-N rational.hypot cascade (isqrt-based, native srmech_rational_sqrt-dispatched, no libm) over the flattened pair, with numpy carrying the array only.
- Routes all 5 DSP magnitude callsites off
np.hypot:fsk/mlse/psk_qam(nearest-symbol decision-region distances),ofdm(channel magnitude),spectral(coefficient magnitude). The numpy-math ratchet'sufuncceiling drops 48 → 43. Round-off-faithful to numpy (rational sqrt is floor-projected vs IEEE round-to-nearest — a ≤1-ULP shift that never flips a nearest-symbol / argmax decision), and bit-exact wheneveraᵢ² + bᵢ²is a perfect square (Pythagorean-triple constellation points decode exactly). - 1 new
srmech.amsc.laplacian.elementwise_hypotToolEntry →describe()["tools"]["total"]282 → 283;composition_of_crosetta bucket (composes thec_dispatchedrational.hypot/srmech_rational_sqrt; no own C symbol). Added to__all__+LAPLACIAN_OPS. Class N (rational magnitude) over a Class-L array surface; numpy carriers-only. ABI 3.
[0.7.5rc51] - 2026-06-09¶
laplacian.dense_outer_{complex,real} — the np.outer → cascade decrement (numpy-removal PHASE A). Resumes the numpy-math carrier-removal sweep with the deferred outer-product op. An outer product a ⊗ b (out[i,j] = aᵢbⱼ) IS the k=1 case of a matrix product — a a column, b a row — so dense_outer_complex is exactly dense_matmul_complex on the reshaped pair: it rides the native srmech_dense_matmul_complex kernel with no inner summation (each entry a single multiply), making it bit-identical to numpy's outer product. dense_outer_real is the complex kernel on imag-free input → float64.
- Routes the 3 genuine
np.outercallsites off numpy's contraction engine onto the cascade:qm.propagators' two realkᵘkᵛmomentum tensors (Feynman photon + massive-vector propagators) →dense_outer_real, andqm.single_particle's complex|ψ⟩⟨ψ|density matrix →dense_outer_complex. All three are bit-exact (rank-1, single-multiply per entry — no round-off shift). The numpy-math ratchet'smatmulceiling drops 51 → 48; the 2 remainingnp.outerare thematrix_cascadesQR-internal Householder updates, still on the shape-polymorphic pass. - 2 new
srmech.amsc.laplacian.dense_outer_{complex,real}ToolEntries →describe()["tools"]["total"]280 → 282; bothcomposition_of_crosetta bucket (compose thec_dispatcheddense-matmul kernel; no own C symbol). Added to__all__+LAPLACIAN_OPS. Class L (rank-1 contraction); numpy carriers-only. ABI 3.
[0.7.5rc50] - 2026-06-09¶
amsc.text.{tokenize, cooccurrence_edges} — the §40 R3-U1 acceptance fix (was SHIPPED-but-FAILING 3/3; F722). The rc43 text→graph leaves shipped in srmech.amsc.laplacian but failed the RBS-LM §40 acceptance bar on all three points. rc50 relocates them to a dedicated srmech.amsc.text ingestion module (so laplacian stays purely spectral — §40 Option 1) and fixes every point:
- #1 Unicode (F698) —
tokenizenow keeps runs of Unicode letter|mark codepoints (unicodedata.category(ch)[0] in ("L","M")) and casefolds, socafé/Москва/naïve/日本語survive intact. The rc43 ASCII\w+gave['caf','na','ve'](accents stripped, Cyrillic/CJK dropped) — fixed. NFC-normalises by default; word-internal apostrophes kept. - #2 No silent vocab cap (F708) —
cooccurrence_edgeskeeps the full ranked vocabulary by default; the rc43 silentvocab_size=1000default (the pre-encode quantization bug, re-introduced as a default) is gone. A top-K cap is now an explicit, logged caller opt-in (vocab_size=N→ logs the dropped count). The 256 native bound is for the dense-eig block only, never the vocabulary. - #3 Document-boundary window reset — co-occurrence never crosses a document boundary.
docsis aSequence[Sequence[str]](one inner sequence per document → the window resets per document); a flatSequence[str]is treated as a single document (back-compat). A newvocab=param accepts an explicit ranked vocabulary. - Signatures:
tokenize(text, *, stoplist=DEFAULT_STOPLIST, unicode_normalize=True) -> list[str](newDEFAULT_STOPLISTof function words incl. the F714-leaked prepositions; passstoplist=Nonefor raw mode) andcooccurrence_edges(docs, *, window=2, vocab=None, vocab_size=None) -> (n, edges, weights). - Relocation, not addition: the two
ToolEntrys movesrmech.amsc.laplacian.* → srmech.amsc.text.*(categorytext); the tworosetta_classification.ndjsonlines re-point (non_computebucket unchanged).describe()["tools"]["total"]stays 280 (no new op). The rc43laplaciantokenize/cooccurrence_edges/DEFAULT_TOKEN_PATTERN+ theirre/collectionsimports are removed;laplacianis purely spectral again. Class B/G ∘ Class-L precursor; pure-Python, numpy-free. ABI 3.
[0.7.5rc49] - 2026-06-09¶
dsl.generate_class_descriptor — the make_class inverse (RBS-LM UPSTREAM §39). The genome seed proved the forward direction (a [class] TOML → a constructed CatalogClass via make_class); rc49 closes the loop the OTHER way — components (or a constructed class) → a valid [class] TOML, round-trippable straight back through make_class.
generate_class_descriptor(name, *, fields=None, methods=None, doc=None, kind=None)→ a[class]TOML string. Two modes: explicit — passfields({field: type}) +methods({method: {op: dotted-cascade-op, binds: [...], doc, appends|sets}}, thedescribe_classmethod shape) and it renders straight from the components; introspection — pass ONLYnameof a registered class (e.g."Genome") and it recovers the descriptor viadescribe_classand re-emits it (a constructed class rendering its OWN[class]TOML back out — the Class-H self-introspection path).doc/kindoverride the introspected values when given.- Round-trip-exact: docs re-emit as single-line basic strings with escaped newlines, so a multi-line seed
docdecodes back bit-identically; drop the emitted string in aregister_class_dirdir andmake_classconstructs the identical class. Bare-key-safe field/method names stay unquoted; others are quoted in the table header. Validates: non-emptyname, each method has a non-emptyop, at most one ofappends/sets. - New
srmech.dsl.generate_class_descriptorToolEntry →describe()["tools"]["total"]279 → 280 (asrmech.dsl.*discovery/render callable — bumps the tool count, SKIPSrosetta_classification.ndjsonlike itslist_ops/describe_classpeers; not anamsccompute op). Framework reading: Class E (catalog enumeration) ∘ Class F (descriptor render) ∘ Class H (self-introspection) — no new primitive class. ABI 3; numpy-free.
[0.7.5rc48] - 2026-06-09¶
laplacian.spectral_block_dispatch — the 1024-node 4-sector spectral one-call (RBS-LM UPSTREAM Ask-3; F233 4-rung). Wires the threaded-Klein-4-streams pattern (the same 4-way fan-out as cascade.parallel_sector_dispatch, but over DISTINCT spectral blocks rather than chirality-transforms of one input) to eigendecompose ≤4 dense symmetric blocks (each n ≤ MAX_NATIVE_NODES = 256) in parallel — 4 × 256 = 1024 nodes within the native dense-eig bound.
spectral_block_dispatch(blocks, *, max_sweeps=100, tolerance=1e-12, combine=True)runsjacobi_eigvalson each block on its own thread of a 4-worker pool. Each worker reads ONLY its own block (0 cross-thread reads), so the parallel spectrum equals the serial spectrum bit-for-bit; wall-clock overlap depends on the native GIL-release / free-threaded build. Returns{ok, n_blocks, block_sizes, n_nodes, blocks: [[eigvals]…], combined: [merged-sorted spectrum]}.- Caps: >4 blocks →
ValueError(the F220 Klein-4 4-cap — 8+ need the order-3 triality); each blockn > 256→ValueError(the per-block dense-eig bound). Class L over the 4-rung parallel dispatch; numpy-free (blocks may belist[list[float]]orndarray). - New
srmech.amsc.laplacian.spectral_block_dispatchToolEntry →describe()["tools"]["total"]278 → 279;composition_of_crosetta bucket (composes thec_dispatchedjacobi_eigvals+ a merge-sort reduction). ABI 3.
[0.7.5rc47] - 2026-06-09¶
hdc.klein4_project_axis — the iω₇-collapse / bipolar projection (RBS-LM §18 Tier-2 leaf; F350/F354). The "asymptotic-DoF render": project a 2-DoF Klein-4 hypervector (γ₅ ⊕ iω₇) onto ONE chirality axis, collapsing it to a 1-DoF bipolar {-1,+1} vector. This is the F350 bipolar render that drops the OTHER axis — and with it that axis's self-error-correction (F354 axis-split: the collapsed observer is structurally blind to errors on the projected-out axis).
klein4_project_axis(v, *, axis="gamma5")→list[int]of{-1, +1}. Per-element bit→sign: clear bit →+1, set bit →-1(the Class-K bipolar sign render; noabs()).axisis a CO-EQUAL, non-privileged convention per the settable-chirality discipline — both"gamma5"(bit 1) and"iomega7"(bit 0) first-class, related by a Class-K axis swap; the default"gamma5"is the surviving-axis of the F354 collapse (a documented convention, not a privileged truth). Class K (asymptotic-DoF render) ∘ Class C (axis select); numpy-free pure bit ops.- New
srmech.amsc.hdc.klein4_project_axisToolEntry →describe()["tools"]["total"]277 → 278;non_computerosetta bucket (a one-way bipolar render/readout out of the store — peer to the rc43tokenize/cooccurrence_edgesprojections, NOT a store-transform wanting a C twin). ABI 3; numpy-free.
[0.7.5rc46] - 2026-06-09¶
Catalog→DSL auto-registration bridge actually routes (RBS-LM UPSTREAM §17 U4). rc45 shipped the U3 list_ops() surface with auto-discovery of registered attested sources — but the auto-discovery read the WRONG source-key field (source_key / name) when list_attested_sources() returns each source under key. So the catalog-chain half was always empty: the packaged attested sources that declare [[catalog.operator_chain]] entries never surfaced. rc46 fixes the field.
list_ops()now surfaces the 7 packaged catalog-chains (asymptotic_calculus ×5, cosmos_validation ×1, pi_digits ×1), each taggedprovenance="catalog:<source_key>",kind="catalog-chain"— and any freshlyregister_attested_root-ed catalog's declared chains too. This is the §17 U4 "one path, not three doors" bridge realized end-to-end:register_attested_root→list_opswith no explicitsource_keys.- Pure registry-read fix; no new public callable.
describe()["tools"]["total"]unchanged at 277. ABI 3; numpy-free. Newtests/test_dsl_list_ops_u4_rc46.pypins the bridge (packaged chains surface,source_keysfilter, end-to-endregister_attested_rootround-trip).
[0.7.5rc45] - 2026-06-09¶
srmech.dsl.list_ops — unified op-discovery across BOTH registries (RBS-LM UPSTREAM §17 U3). Until now the DSL exposed two disjoint op-discovery surfaces: list_catalog_ops() enumerated the value-transform cascade ops, while srmech.amsc.catalog.list_catalog_chains(source_key) enumerated the AMSC catalog-declared operator chains — a kernel chain declared on a text-catalog was invisible to anyone reading the DSL op list. list_ops() unifies them into ONE call.
- Every record carries a uniform
{name, class, purpose, kind, provenance}shape.kindis"stage"/"combinator"(cascade-ops, fromlist_catalog_ops) or"catalog-chain"(catalog-declared chains);provenanceis"srmech"/"user"/"catalog:<source_key>". Sorted by(kind, name). list_ops(source_keys=[...])restricts the catalog-chain half; omitsource_keysto auto-discover every registered attested source. A base install with no catalog registered returns just the cascade-ops (the catalog-chain half is empty — correct), and an unknown source key is tolerated (no chains, no raise).- New
srmech.dsl.list_opsToolEntry (a DSL discovery callable; no rosetta line) →describe()["tools"]["total"]276 → 277. Framework reading: Class E (catalog enumeration) over both registries at once. ABI 3; numpy-free (pure registry read).
[0.7.5rc44] - 2026-06-09¶
DSL dotted-op= resolver + encode_loe_content registered as a cascade-op (RBS-LM UPSTREAM §17 U2). A cascade-catalog descriptor may now name a dotted entry point — [cascade].op = "srmech.signal_processing.encode_loe_content" — so an EXISTING op that lives outside srmech.amsc.cascade is DSL-registrable without re-exporting it. Mirrors the rc39 class-catalog's dotted-path method resolution.
- The verified text→instrument encoder
encode_loe_content(Class A∘C∘M,str → D-bit fingerprint) is now a one-line DSL stage:chain().then("encode_loe_content", D=…)/[[stage]] op="encode_loe_content". Any catalog's text rows get a one-line kernel chain — the §17 U2 "cheapest, highest-leverage" win (the primitive already worked; this makes it nameable in a chain). list_cascade_ops()14 → 15; thelist_catalog_ops/run_toml_chaintool-schema summaries cite the live 15-op count. No newsrmech.amsc.*callable (the op already ships;encode_loe_contentkeeps its existingcomposition_of_crosetta line) →describe()["tools"]["total"]unchanged. ABI 3; numpy-tier (the encoder issrmech[scientific]).
[0.7.5rc43] - 2026-06-09¶
Text→graph stage primitives — the K1 chain's missing front (RBS-LM UPSTREAM §17 U1). srmech.amsc.laplacian gains the two ops that were the only links between raw text and the already-shipped dense_laplacian:
tokenize(text, *, stopwords=None, min_len=2, pattern=None)→list[str]— Class B/G text-segmentation: apply a letter-led word pattern (default[A-Za-z][A-Za-z0-9_-]+), lowercase, drop tokens shorter thanmin_lenor instopwords(case-insensitive).cooccurrence_edges(tokens, *, window=5, vocab_size=1000)→(n, edges, weights)— Class-L precursor: keep thevocab_sizemost-frequent tokens as nodes0..n-1, count unordered co-occurring pairs within a slidingwindow. Returns exactly the tripledense_laplacian(n, edges, weights)consumes; weights are integer counts (exact — floats are for the FPU lift, none here).
from srmech.amsc.laplacian import tokenize, cooccurrence_edges, dense_laplacian
toks = tokenize(doc, stopwords={"the", "a", "is"})
n, edges, weights = cooccurrence_edges(toks, window=5, vocab_size=1000)
L = dense_laplacian(n, edges, weights) # K1 text→graph→spectral, end-to-end
With these, the K1 presence-kernel is an authorable composite end-to-end (tokenize → cooccurrence_edges → dense_laplacian → eigendecompose → …), retiring the hand-rolled re.findall + Counter() co-occurrence idiom. Both pure-Python, numpy-free, deterministic. 2 ToolEntries (tools.total 274 → 276; 5 count-tests bumped); both non_compute in the Rosetta ledger. The directed sibling (the i(A−Aᵀ) Hermitian-Laplacian builder, reusing the shipped hermitian_eigendecompose) is a separate, queued Class-L precursor. ABI 3; numpy not required.
[0.7.5rc42] - 2026-06-09¶
Genome-storage surface, brick 3 — the multi-kernel genome + partition (the F715 hierarchy closes; #962 Part 2). rc37/rc38 shipped the chromosome (one kernel); rc42 ships the genome itself — many kernels, telomere-partitioned, on ONE strand:
srmech.amsc.genome.genome(kernels, the_one)— packs a{label: leaves}mapping (or(label, leaves)pairs) into a single strand: each kernel becomes a telomere-cappedchromosome(coupled throughthe_one), all concatenated — the chromosome set. The per-kernel telomere caps delimit + protect the partitions, so one strand holds many kernels.srmech.amsc.genome.partition(strand, the_one, labels)— the inverse: knows ALL the caps, so (unlike a single-caprecall) it never mistakes one chromosome's cap for another's data. Returns{label: leaves}, each leaf re-bound throughthe_one(the reversiblequad_turn). Round-trips every kernel exactly.
from srmech.amsc.genome import genome, partition
strand = genome({"astronomy": A, "geography": G, "music": M}, one)
partition(strand, one, ["astronomy", "geography", "music"]) == {"astronomy": A, "geography": G, "music": M}
The seed Genome [class] (rc39) gains assemble (→ genome) + partition methods, so the multi-kernel genome is driven end-to-end from the declarative class surface (g.assemble(kernels=…) binds the_one from the field). This completes the F715 storage object: GENOME (multi-kernel) → CHROMOSOMES (telomere-capped) → helix of QUAD-TURNS → LEAF ≤ 256. 2 ToolEntries (srmech.amsc.genome.genome / partition; tools.total 272 → 274); both composition_of_c in the Rosetta ledger. ABI 3; numpy not required.
[0.7.5rc41] - 2026-06-09¶
CLI + tool_schema/introspect class-awareness — the genome surface closes (#962 Part 2). The user-declared [class] surface (rc39/rc40) is now reachable from the shell, the package's self-description, and the LLM tool list:
srmech class list/srmech class describe NAME— the CLI discovery face (srmech.cli.klass; module isklassbecauseclassis a keyword, the CLI token isclass).listenumerates the seedGenome+ any bring-your-own classes;describeprints the full JSON descriptor.introspect.describe()["classes"]—{"total", "names"}; the package now recognises its own user-extensible class surface (the Class-H self-recognition view). A sibling key —tools.totalis unchanged by it.- 2 ToolEntries
srmech.dsl.list_class_surface/describe_class— the LLM tool list now includes class discovery (tools.total270 → 272).
This completes the class-from-TOML arc: rc37/rc38 the genome cascade primitives → rc39 the [class] loader + CatalogClass → rc40 the DSL one-shot run/introspect surface → rc41 CLI + tool_schema/MCP. A researcher authors a [class] TOML and reaches it from Python (make_class / run_class_method), the shell (srmech class …), and an LLM agent (the tool list + describe()). ABI 3; numpy not required.
[0.7.5rc40] - 2026-06-09¶
DSL class-awareness — the one-shot introspect + run surface for user-declared classes (#962 Part 2). The rc39 [class] loader gets the DSL-layer surface the CLI + tool_schema/MCP compose on (rc41):
srmech.dsl.describe_class(name)/list_class_surface()— a JSON-able view of a user class (fields + methods +binds+appends/sets+ provenance), so an LLM / CLI knows what a class offers before calling. Mirrorslist_catalog_opsfor the op surface.srmech.dsl.run_class_method(class_name, method, *, fields=None, args=None)— the stateless one-shot run: construct a fresh instance from thefieldsdict, invokemethodwith theargsdict, return{"class", "method", "result", "fields"}wherefieldsis the post-call state (so anappends/setsmutation is visible). The caller threads state across calls — the functional form of the name+UUID handle grammar;fields/argsare plain dicts (MCP-grammar friendly, no**kwargs).
from srmech.dsl import run_class_method
run_class_method("Genome", "shape", fields={"the_one": one}, args={"n": 5000})
# -> {"class": "Genome", "method": "shape", "result": {"shape": "quad_strand", ...}, "fields": {...}}
Lives in srmech.dsl (orchestration over the rc39 CatalogClass) — no new srmech.amsc/qm callable, no tool-schema / rosetta entry. CLI subcommands + tool_schema/MCP registration of user classes are rc41. ABI 3; numpy not required.
[0.7.5rc39] - 2026-06-09¶
User-declared classes from [class] TOML — the cascade-catalog config-driven pattern lifted from ops to classes (#962 Part 2; user direction 2026-06-09). A researcher authors a [class] descriptor (fields + methods-as-cascade-op-refs) and srmech's config-driven loader constructs a generic, class-aware object — zero user Python, 100% declarative. This is how an end user targets srmech for their own research domain.
srmech.dsl.make_class(name)→ a factory for the declared class;CatalogClassis the generic runtime object (fields hold state; declared methods dispatch to cascade ops resolved by dotted srmech path). A method'sbindsnames resolve positionally from the call kwargs first, then the instance fields; leftover kwargs pass through (e.g.label=);appends/setsroute the op result back into a field.register_class_dir(path)/SRMECH_CLASS_PATH— bring-your-own: drop a[class]TOML in your dir and it constructs identically to the shipped seed (B-tier, attested to the descriptor hash; a user class-name may not shadow a shipped one). Mirrorsregister_catalog_direxactly — the op-level surface lifted to classes.- Plus
list_classes()/get_class_descriptor(name)/load_class_catalog().
genome/chromosome/telomere ships as the built-in seed worked-instance (_research/class_catalog/genome.toml): Genome(the_one=...) with methods shape / cap / add_chromosome / recall that bind to the rc37/rc38 srmech.amsc.genome.* flat functions. So:
from srmech.dsl import make_class
g = make_class("Genome")(the_one=one)
g.shape(n=5000) # -> {'shape': 'quad_strand', 'depth': 3, ...}
strand = g.add_chromosome(leaves=[...], label="astronomy") # coupled + appended
leaves = g.recall(strand=strand, telomere=g.cap(label="astronomy")) # exact round-trip
DSL stage-awareness, the CLI surface, and tool_schema/MCP introspection of user classes are the next bricks (rc40/rc41). The loader lives in srmech.dsl (no new srmech.amsc/qm public callable → no tool-schema count change). ABI 3; numpy not required.
[0.7.5rc38] - 2026-06-09¶
Genome-storage surface, brick 2 — the chromosome (telomere / chromosome / recall). The LAYER-1 cascade primitives that the upcoming user-authored class layer binds to. srmech.amsc.genome gains:
telomere(label, dim=64)— the non-data content-address cap that delimits a chromosome (biology's repetitive non-coding chromosome-end cap). A deterministic, content-addressed Klein-4 sentinel:sha256_bytes(label)→ a seed → a Klein-4 carrier. Same label → same cap (so a chromosome is recalled/partitioned by matching it), distinct labels → distinct caps. Class A (content-address) ∘ Class M (Klein-4 carrier).chromosome(leaves, the_one, *, label=...)— pack one kernel into a telomere-capped strand: a helix of quad-turns (each leaf coupled throughthe_onevia the reversiblequad_turn), led by a telomere cap. Returns[cap, coupled_turn0, coupled_turn1, ...].recall(strand, the_one, telomere)— the exact inverse ofchromosome: skip every element equal to the cap (matched by value, so it generalises to a multi-chromosome genome strand) and re-bindthe_oneto recover each leaf.recall(chromosome(L, one, label=K), one, telomere(K, len(one))) == L.
This is the substrate for the next bricks: a user-authored class-descriptor TOML (declarative [class] block — fields + methods-as-cascade-op-refs) that srmech's config-driven loader constructs into a generic class-aware object, with DSL / CLI / tool_schema made class-aware (the cascade-catalog register_catalog_dir pattern lifted from ops to classes). genome/chromosome/telomere is the seed worked-instance; these three functions are the ops a Genome class's methods bind to. Full registry gates: 3 ToolEntrys (category genome; tools.total 267→270), 3 rosetta_classification.ndjson lines (all composition_of_c). ABI 3; numpy not required.
[0.7.5rc37] - 2026-06-09¶
Genome-storage surface, brick 1 — biological-structure names as cascade names (#962 Part 2; genome / chromosome / telomere / quad-strand). New module srmech.amsc.genome (validated as F711–F715 on the research subtree; the substrate-self-recognition reading — biology is one substrate-class, so the names of the biological structures are the cascade names of the storage object). This brick ships the two foundational ops:
encode_shape(n)— the encode criterion (F715):n ≤ 256→ atome(one dense 2⁸ leaf) ·n ≤ 1024→ amobius(one quad-turn = the 4 Klein-4 sectors) ·n > 1024→ aquad_strand(a helix of quad-turns, a chromosome).depth = ceil(log4(ceil(n/256)))is the number of base-4 quad levels overleaves = ceil(n/256), computed in pure integer arithmetic (Class I/N; no floatlog— "floats are for the FPU lift"). Thresholds attested to256 = 2⁸and the Klein-4 order4— no magic. Reproduces F715's table to the byte (200→tome, 800→mobius, 5000→depth 3, 1.77M→depth 7).quad_turn(turn, the_one)— the helix-turn coupling (F713): bind a turn throughthe_one(the held invariant) by the reversible Klein-4 bind (V4 = (F₂)² XOR, soquad_turn(quad_turn(t, one), one) == t) — the duality held WITHOUT collapse, numpy-free.the_oneis the shared invariant in every turn's coupling, so a chromosome navigates across its turns throughthe_oneand recovers any turn by re-binding.
Honest caveat (F712): cascade.parallel_sector_dispatch is single-level (CAP = 4 = the Klein-4 order); the quad-turn is one chirality level — the deeper leaf-tree is base-4 radix addressing, not more chirality dispatch. The chromosome (telomere-capped strand) and the genome (multi-kernel, telomere-partitioned) assemble in subsequent rc bricks. Full registry gates: 2 ToolEntrys (category genome; tools.total 265→267), 2 rosetta_classification.ndjson lines (encode_shape→non_compute, quad_turn→composition_of_c). ABI 3; numpy not required.
[0.7.5rc36] - 2026-06-09¶
Laplacian tidy — numpy-free native dispatch for the Class-L graph build ops + a stale-comment fix (UPSTREAM §38, completing rc35). The integration tracker (#962) Part-1 asked for numpy-free native dispatch across the Class-L surface; rc35 did jacobi_eigvals (the 49× eig). rc36 finishes the build side: dense_adjacency / dense_laplacian / normalized_laplacian now reach the bound srmech_graph_* C symbol on the numpy-absent install too, via a new _build_matrix_native_listmarshal (Python list → flat ctypes uint32/double buffers → reshape), falling back to the pure-Python builder only when there's no native lib / n > 256 / non-OK. (The build is O(edges)-cheap — this is the carrier-removal consistency win, not the perf-critical eig.) Also corrects the module-header "wrappers fall back to numpy unconditionally" note, now inaccurate post-rc35: eigvals dispatch numpy-free; only the eigenvector decomposition (symmetric_/hermitian_eigendecompose) stays the LAPACK eigh path, by design (eigenvector sign / degenerate-subspace rotation is non-unique).
This is PHASE B of the numpy-carrier-removal north-star. Re #962: the binding ask was already satisfied at HEAD (the symbols are bound); klein4→C stays deferred behind W5 (whose even-count semantics are now documented — strict majority, tie→0); the Klein-4 spectral quad-stream is sequenced into the Part-2 genome-storage surface (the quad-turn is its structural unit). Test-only ceiling untouched; ABI 3; no public-surface change.
[0.7.5rc35] - 2026-06-09¶
numpy-free native dispatch for jacobi_eigvals — the C eig is reachable without numpy now (~49×; UPSTREAM §38 / F708). The rc28 long-run bug-test session found (UPSTREAM_NOTES §36/§37/§38 on PR #687) that the numpy-free Class-L store ran ~45–68 s at n=256 because laplacian.jacobi_eigvals early-returned the pure-Python Jacobi cascade whenever numpy was absent — even though the srmech_jacobi_eigvals C symbol is bound in the shim and a direct ctypes call runs in 1.4 s (~49×). The symbol was reachable; the wrapper just didn't marshal to it without numpy.
rc35 adds _jacobi_eigvals_native_listmarshal — a numpy-free marshalling path that builds a flat (c_double * n·n) ctypes buffer straight from the list[list[float]] and calls the bound C symbol. The numpy-absent branch now dispatches to it when HAS_NATIVE and n ≤ MAX_NATIVE_NODES, falling back to the pure-Python Jacobi cascade only when there's no native lib / n too large / non-OK status. No numpy required for the native eig. This is PHASE B of the numpy-carrier-removal north-star (the C foundation must be numpy-free-reachable before numpy can leave as a carrier).
Scope: §38's "bind the symbols" ask was already satisfied (HEAD _native.py declares argtypes for jacobi/laplacian/hermitian/hdc/klein4 — §38 counted the 13 *_c convenience wrappers, not the bindings); bytes-hdc already dispatches numpy-free; the symmetric_eigendecompose eigenvector path stays numpy.linalg.eigh (deliberate — eigenvector parity is non-unique); klein4→C stays deferred behind W5. ABI 3; no public-surface change.
[0.7.5rc34] - 2026-06-09¶
Matmul-ledger decrement — route np.kron + np.einsum callsites onto the existing cascades. Resumes the numpy-math matmul migration (#928) by pure routing — no new public op, no registry gates — onto the already-shipped, already-registered kron / einsum cascades:
qm.so8's su(3) commutant superoperatorad⊗I − I⊗ad(×2) andqm.bell's_kronCHSH-operator builder (×1) →srmech.amsc.cascade.spectral_cascades.kron(the Class-I mixed-radix-index ∘ Class-M Kronecker cascade;np.asarrayis carrier-only).qm.triality's octonion-couple(x*y)_k = Σ x_i y_j C[i,j,k](×1) →srmech.amsc.cascade.matrix_cascades.einsum.
All four are value-faithful — so8 and triality bit-exact (err 0.0), bell bit-exact on the Pauli / measurement operators it actually uses. CEIL_MATMUL 55 → 51. Deferred (own passes): ica_jade's 2 einsum (hot Jacobi sweep loop — perf-careful), the np.outer family (awaits a dense_outer cascade), and the matrix_cascades QR-internal vdot/outer/@ (shape-polymorphic pass). No public surface change, ABI 3.
[0.7.5rc33] - 2026-06-09¶
A-N cascade ratchet CLOSED to zero — the bare-Python tier joins the C tier at no continuous-math residue. The rc32 ratchet pinned one site: the cmath.sqrt Wilkinson-shift discriminant in the float eigvals (complex-spectrum shifted-QR path). rc33 routes it through a new matrix_cascades._complex_sqrt — the principal complex root rebuilt from the Class-N hypot/sqrt real cascades joined by a Class-K sign-branch (principal branch Re ≥ 0), with a Class-K pin-slot-at-zero floor on each radicand (the _norm2 idiom — a tiny <0 from float round-off pins to 0). No libm cmath; the import cmath is gone. Matches cmath.sqrt to ~1e-13, so the float eigvals stays round-off-faithful to numpy.
All three A-N-ratchet categories are now 0 (transcendental 1 → 0, math_const 0, float_pow 0): the bare-Python tier reaches the same zero-residue the C-transpile arc drove libsrmech to (libm 23 → 0). Every continuous-math op in srmech — C, numpy-tier, and now bare-Python — is a cascade of the 14, floats only at the FPU lift. Test-only ceiling drop + one helper; no public surface change, ABI 3. SSoT: #928.
[0.7.5rc32] - 2026-06-09¶
A-N cascade ratchet — the down-only guard for continuous math not yet reduced to the 14 A-N cascades. Per user direction ("add another code sweep to check for math that has not been reduced to cyclic algebra … A-N cascades might be the right way to put it"), tests/test_an_cascade_ratchet.py is the bare-Python-tier sibling of the C-transpile libm ratchet (drove libsrmech 23 → 0) and the numpy-math ratchet (the np.linalg/np.fft/@/ufunc tier). It pins the third tier: bare-Python math.*/cmath.* float transcendentals, math.pi/math.tau float constants, and fractional ** <float> powers — every one of which has an exact A-N cascade replacement (srmech.amsc.rational.{sin,cos,atan,exp,log,sqrt,hypot} / pi_cascade_digits / integer power). Per "floats are for FPU lift", each such call where a cascade belongs is a defect.
AST, not regex (the load-bearing design point): the source carries ~39 descriptive docstring/summary-string mentions of math.cos etc. ("Substrate-native replacement for math.cos"); a text regex would count every one as debt. This walks the AST and counts only genuine Call/Attribute/BinOp nodes — strings/comments are invisible (the same basis as the no-abs scanner). Three tight down-only categories. Baseline: transcendental = 1 (the lone cmath.sqrt Wilkinson-shift discriminant in the float eigvals complex-spectrum path — eliminated when complex-eigenvalue exact isolation lands, the rc31 follow-up); math_const = 0 (the π-cascade discipline fully holds — zero float math.pi in any compute path); float_pow = 0. Excluded (documented, not silent): integer primitives (isqrt/gcd/factorial/…), IEEE/sign primitives (copysign/fabs/isfinite/… — the Class-K sign family; copysign at an IEEE ±0 limit is the correct idiom), and the inherently-invisible string mentions. No module is carved out — a stray math.sin anywhere fails.
Test-only; no public surface, no ToolEntry, no introspect change, ABI 3. SSoT: #928.
[0.7.5rc31] - 2026-06-09¶
Exact-until-rotation EIGENVALUES — the ill-conditioned problem does have a cascade form; #928. The eig/svd "lift" looked qualitatively different from the DFT's (eigenvalues are irrational; float root-finding from char-poly coefficients is Wilkinson-ill-conditioned). But per user insight, that ill-conditioning is a float-perturbation artifact, not inherent — the eigenvalues of an integer matrix are algebraic, and kept in exact arithmetic the whole way they come out well-conditioned. Two new public cascade ops:
char_poly— the exact integer characteristic polynomialdet(xI - A)(Faddeev–Leverrier, arbitrary-precision integer): the exact ALGEBRAIC substrate of the eigenproblem — exact trace (= -c1), exact determinant (= (-1)^n·cn), all elementary symmetric functions of the spectrum, no floating point. Class L ∘ M ∘ K.eigvals_exact— exact REAL eigenvalues (with multiplicity) via the well-conditioned cascade:char_poly→ Yun square-free factorisation → Sturm sign-sequence isolation (Class C sign-count at Class K interval boundaries) → rational bisection (Class N anchors → the algebraic asymptote), all in exactFractionarithmetic, then one FPU lift.bitssets refinement precision;return_intervals=Trueyields exact(lo, hi)rational isolating intervals. Each eigenvalue stays an exact algebraic number until the rotation to the observable.
Proven: Wilkinson diag(1..10) comes out exact (err 0.0) where float np.roots on the same exact char-poly loses ~9 digits; irrational golden-ratio eigenvalues exact to 15 digits; repeated eigenvalues carry correct multiplicity; and singular values via the exact integer Gram AᵀA match numpy to ~1e-9 — so this is the exact substrate of svd too. The existing float eigvals (shifted-QR, complex spectrum) stays; complex-eigenvalue exact isolation is the follow-up.
Both ops are pure-Python arbitrary-precision (bignum_reference rosetta bucket — non-debt; numpy is a container only). 2 ToolEntries (introspect tools.total 263 → 265), 2 rosetta lines. ABI 3; the numpy-math ratchet is untouched.
SSoT: issue #928; test_eigvals_exact_rc31.py.
[0.7.5rc30] - 2026-06-09¶
The exact DFT goes general-N — every integer signal, not just power-of-two; #928. rc28/rc29 handled the power-of-two case via the negacyclic ring (ζ^{N/2} = -1). rc30 extends exact-until-rotation to any length via the full cyclotomic ring ℤ[ζ_N] = ℤ[x]/Φ_N(x):
- A pure-integer cyclotomic engine computes
Φ_N(fromx^N - 1 = Π_{d|N} Φ_d, recursive exact integer polynomial division) and a reduction table mapping eachζ^jto the length-φ(N)power basis (cached perN). Each twiddleζ^{nk mod N}is reduced and accumulated — still no floats, one FPU lift at the end. exact_dft/exact_idftnow accept anyN ≥ 2(the spectrum coefficient vectors have lengthφ(N);= N/2for power-of-twoN).liftinfers the basis degree from the spectrum, so it handles both.dft/fftroute all integer / Gaussian-integer signals (anyN) through the exact path — the power-of-two case keeps the fast negacyclic / native-C route, the general case uses the arbitrary-precision Python cyclotomic path.
No new public surface, no ratchet movement. Python-only (the general path has no fixed-width C twin → the bignum_reference shape; the op stays c_dispatched for its power-of-two C fast path). ABI 3. The numpy-math ratchet is untouched (no numpy). The rc29 exact_dft non-power-of-two rejection test and the rc28 non-power-of-two not-exact-routed test are updated to the new general-N contract (an intentional behaviour change). Performance note: the general (non-power-of-two) path is O(N²·φ(N)) integer work — exactness costs the φ(N) factor; the float cexp path still serves float inputs.
SSoT: issue #928; test_exact_dft_general_n_rc30.py.
[0.7.5rc29] - 2026-06-09¶
The exact ℤ[ζ_N] spectrum goes public, with a native-C twin; #928. rc28 introduced the exact cyclotomic-integer DFT as a private engine behind dft/fft. rc29 promotes it to a first-class introspected op and gives it a C peer (the "every primitive earns a C surface" commitment):
srmech.amsc.cascade.exact_dft/exact_idft— return the exactℤ[ζ_N]integer spectrum of a power-of-two integer / Gaussian-integer signal (one integer(real_vec, imag_vec)pair per bin) — no floats.liftis the single FPU rotationℤ[ζ_N] → ℂ(the only float producer).- Native-C twin
srmech_exact_dft_i64(c/src/srmech_exact_dft.c, JPL-clean, caller-buffer int64): the integer add/subtract fast path. The Python op dispatches to it whenN·max|signal|is int64-safe, and falls back to the arbitrary-precision bignum path otherwise.
Rosetta: exact_dft / exact_idft classify c_dispatched (they have the C twin); lift is composition_of_c (over the Class-N cexp). Three new ToolEntries (introspect tools.total 260 → 263), three rosetta lines, a list[tuple[list[int], list[int]]] MCP coercer for the spectrum param. ABI stays 3 (additive symbol). The numpy-math ratchet is untouched (no numpy). General-N (non-power-of-two) cyclotomic reduction is the next follow-up.
SSoT: issue #928; test_exact_dft_public_rc29.py (public contract + native==Python bit-exact parity).
[0.7.5rc28] - 2026-06-08¶
The first exact-until-rotation cascade — the DFT/FFT goes integer (cyclotomic ℤ[ζ_N]) until one FPU lift; #928. Per user direction — "don't use floats for bit-exact math, that's what ints and complex are for; floats are for FPU lift." A DFT's twiddles e^{-2πi·j/N} are roots of unity = algebraic integers in ℤ[ζ_N]; for power-of-two N the cyclotomic polynomial is Φ_N(x) = x^{N/2}+1, so ζ^{N/2} = -1 (a Class K pin-slot sign-flip, never abs) and the ring collapses to the negacyclic integers ℤ[x]/(x^{N/2}+1) — a length-N/2 integer vector. The DFT of an integer / Gaussian-integer signal is then pure integer add/subtract: bit-for-bit deterministic, with floats produced exactly once at the final FPU lift ζ → e^{-2πi/N} (the projection from the discrete substrate to the continuous observable). This is more faithful than a float FFT, which rounds at every butterfly — it rounds once.
This sharpens rc27's framing: the DFT/FFT cascade is no longer merely "round-off-faithful to numpy" for integer input — it is exact-until-rotation, the substrate-native pattern made concrete.
- New internal engine
srmech.amsc.cascade.exact_dft(private helpers_exact_dft_core/_lift_spectrum/_try_int_pairs/_exact_transform): the exact cyclotomic-integer transform + the single FPU lift (reusing the Class-Ncexp, numpy-absent-safe). spectral_cascades.dft/fftnow route an all-integer / Gaussian-integer power-of-two signal through that engine (sofftanddftagree bit-for-bit on integer input, and both are ≤1e-15 fromnumpy.fft); float signals (already continuous) and non-power-of-two lengths keep the floatcexppath unchanged.
Zero new public surface; ratchet untouched. No new public introspected callable (the engine is private), so the numpy-math ratchet, the rosetta python_only_debt debt bucket, the tool-schema coverage, and the introspect tool counts are all unchanged. No numpy added; pure Python-tier; no C change, ABI stays 3. Exposing the exact ℤ[ζ_N] spectrum as a public op belongs with its native-C twin (so it lands c_dispatched, not Python-only debt) — the tracked follow-up; general-N (non-power-of-two) cyclotomic reduction is also a follow-up.
SSoT: issue #928; test_exact_dft_rc28.py.
[0.7.5rc27] - 2026-06-08¶
The linalg/fft phase opens — the linear-solve family onto cascades (numpy-math linalg_fft 126 → 122; #928). With the dense-matmul ceiling at its floor, the arc pivots to the larger linalg_fft ceiling (pinned at 126 since rc13). Per user direction — cascade + TOML for all maths; numpy is a carrier only (with the carrier itself removed as the final step, after the maths sweep) — the cascades replace numpy math even where they are not bit-exact: fft (radix-2), svd (Gram-route), qr (Householder), eig (Jacobi) are round-off-faithful to numpy (~1e-14), not bit-identical, and that within-round-off shift is accepted (any bit-equality-vs-numpy test relaxes to a tolerance).
rc27 routes the linear-solve family:
map_ml— the ML/MAP normal-equationnp.linalg.solve(M, …)(×2) →dense_solve(bit-exact for the 1-D RHS both sites have).qm.triality+signal_processing.esprit—np.linalg.lstsq(…)→matrix_cascades.lstsq(round-off-faithful ~5e-16, complex-safe). The cascade returns a bare ndarray, not numpy's(x, residuals, rank, sv)4-tuple, so the callsite unpacking changed (solution, _, _, _ =→solution =;[…][0]→ direct).
numpy-math ratchet linalg_fft 126 → 122. Pure Python-tier; no C change, ABI stays 3. No new public symbols. Not migrated: the cascade ops' own internal numpy kernels (laplacian eigh/solve — the designated Class-L implementations, which have pure-Python fallbacks; a deeper separate pass), and the docstring / ToolEntry-summary numpy.linalg.* cross-reference mentions (precise documentation — left intact, not gamed). The map_ml / triality / esprit suites pass unchanged. Next: np.fft (with n/axis handling) + np.linalg.svd/qr/eigvals + inv/pinv.
SSoT: issue #928; the numpy-math ratchet (test_numpy_math_ratchet.py).
[0.7.5rc26] - 2026-06-08¶
The matmul-kernel phase, batch 11 — the genuine-code tail of the dense-matmul migration (numpy-math matmul 60 → 55; #928). Five remaining genuine dense-matmul code sites (as opposed to docstring mentions or distinct ops) route onto the cascade helpers:
vector_quantisation— the codebook cross-termvec·cbᵀ(bothfloat64) →dense_matmul_real.sinc_interp— the Whittaker-ShannonK·y, whereKis the real sinc matrix butyiscomplex128(IQ signal) →dense_matvec_complex(genuinely complex — routing through the real helper would have dropped the imaginary part).farrow— the Lagrange fractional-delay tapC[k]·x(real 4-tap dot) →dense_dot_real.qm.potentials— the harmonic-oscillator number operatora†·a(complex ladder ops) →dense_matmul_complex.qm.sm— the CKM-unitarity checkV·Vᴴ(complex) →dense_matmul_complex.
numpy-math ratchet matmul 60 → 55. Pure Python-tier; no C change, ABI stays 3. No new public symbols. This essentially reaches the dense-matmul-migration floor: of the remaining ~55, ~16 are docstring / comment / ToolEntry-summary @ mentions (a cosmetic · reword sweep) and ~25 are distinct ops needing their own cascades (np.convolve, np.correlate, np.kron, np.outer, np.einsum). Values bit-preserved; the vector-quant / sinc / farrow / harmonic-oscillator / CKM suites pass unchanged. The laplacian Schur L_pi·X is deferred (in-helper, shape-polymorphic — its own pass).
SSoT: issue #928; the numpy-math ratchet (test_numpy_math_ratchet.py).
[0.7.5rc25] - 2026-06-08¶
The matmul-kernel phase, batch 10 — the real DSP closed_form_ops cluster (numpy-math matmul 75 → 60; #928). Fifteen contraction sites across the closed-form signal-processing reference ops route onto the cascade helpers:
dct(2) — the DCT-matrix productsarr·Mᵀ(n-D) andM·arr(1-D), whereMis the real cosine basis →dense_matmul_real/dense_matvec_real.map_ml(6) — the ML/MAP normal-equation chainAᵀR_v⁻¹,AᵀR_v⁻¹A,AᵀR_v⁻¹y, andR_x⁻¹μ(allfloat64) →dense_matmul_real/dense_matvec_real. Thenp.linalg.inv/solvestay (linalg-engine surface, a separate ceiling).ica_jade(6) — theXᵀXcovariance, the whiteningdiag(λ^-½)·Vᵀ+W·Xᵀ, and the Givens joint-diagonalisationV·G/Vᵀ·W/W·Xᵀrotations (all real) →dense_matmul_real. The 2np.einsumcumulant-tensor rotations + thenp.linalg.eighstay (distinct ops).fsk(1) — the M-tone correlator banktones·conj(window), wheretonesiscomplex128→dense_matvec_complex(genuinely complex; the result feeds|z| = hypot(re, im)).
numpy-math ratchet matmul 75 → 60. Pure Python-tier; no C change, ABI stays 3. No new public symbols (reuses the rc20/rc21 helpers). These DSP modules import numpy at module top, so the helper import is top-level (unlike the lazy-numpy amsc modules in rc24). Values bit-preserved; the dct / map_ml / ica_jade / fsk suites pass unchanged. The np.convolve/correlate/outer/einsum sites stay — distinct ops for later batches.
SSoT: issue #928; the numpy-math ratchet (test_numpy_math_ratchet.py).
[0.7.5rc24] - 2026-06-08¶
The matmul-kernel phase, batch 9 — the real "Minkowski + real-dot" sweep (numpy-math matmul 86 → 75; #928). Eleven real-typed contraction sites across qm and amsc route onto the real cascade helpers:
qm.relativistic(3) — thek_μ = η_{μν} k^νlowering matvec (eta @ k), the Klein-Gordon⟨k_spatial, k_spatial⟩dispersion dot, and the Lorentz-invariantk² = kᵀ η kbilinear →dense_matvec_real/dense_dot_real.qm.propagators(1) — the photon-propagator gauge-termeta @ klowering matvec →dense_matvec_real.amsc.harmonics(3) — the_spectral_scoresenergy / mirror / three-cycle symmetry probes (⟨x,x⟩,⟨x,x[::-1]⟩,⟨x,roll(x)⟩), each an explicit Class-L inner product →dense_dot_real.amsc.hdc(3) — the Moufang-inverse norm² gates inloop_inv/loop_inv_hd(per-block) and theg2_three_formassociator⟨x, y×z⟩→dense_dot_real.
numpy-math ratchet matmul 86 → 75. Pure Python-tier; no C change, ABI stays 3. No new public symbols (reuses the rc20/rc21 helpers). The amsc sites import dense_dot_real function-locally so harmonics/hdc stay numpy-absent-safe (§22) — numpy is already loaded by the np.asarray(…, dtype=float) that precedes each dot. All values bit-preserved (the helpers route imag-free input through the complex kernel and drop the exactly-zero imaginary part); the qm-relativistic/propagator + hdc-loop + harmonic suites pass unchanged. The np.outer (k^μ k^ν) sites stay — a distinct op for a later batch.
SSoT: issue #928; the numpy-math ratchet (test_numpy_math_ratchet.py).
[0.7.5rc23] - 2026-06-08¶
The matmul-kernel phase, batch 8 — qm.so8 onto the cascade, real + complex (numpy-math matmul 105 → 86; #928). The g₂ / Spin(8) module's 17 contraction sites route through the cascade, split by dtype:
- 15 real sites — the
[X,Y]commutator, the su(3)/g₂ Gram products (su3·su3ᵀ,q_sd·q_sdᵀ, …), the basis-projection matvecs (g2[a]·e_k,matrix·basis[a], the structure-constantpinv·bracket), and the Gram-Schmidtq·residualdot →dense_matmul_real/dense_matvec_real/dense_dot_real. - 2 complex sites — the su(3)-weight Rayleigh quotients
vᴴvandvᴴ·ad·v.vis a complex eigenvector of the realad(H)(eigenvalues ±i·weight), so these are genuinely complex →dense_dot_complex/dense_matvec_complex. (Routing them through the real helpers would have dropped the imaginary part and corrupted the weights.)
numpy-math ratchet matmul 105 → 86. Pure Python-tier; no C change, ABI stays 3. No new public symbols (uses the rc20/rc21 helpers). The so8 parity tests pass unchanged — g₂ = Der(𝕆) dim 14, the 14 = 8 + 3 + 3̄ su(3) branching, and the su(3) weight computation (the complex Rayleigh quotients) all hold. The 2 np.kron (so(8) adjoint tensor) stay — a distinct op for a later batch. Minkowski / DSP real sites land next.
SSoT: issue #928; the numpy-math ratchet (test_numpy_math_ratchet.py).
[0.7.5rc22] - 2026-06-08¶
The matmul-kernel phase, batch 7 — qm.triality real products onto the real cascade (numpy-math matmul 115 → 105; #928). First consumer of the rc21 real trio.
qm.triality(7 sites) — the octonion-rep matvecs (operator @ octonion_mul(…),g_v/g_s/g_c @ …) route throughdense_matvec_real; the 28×28 Spin(8) triality productstau = S_B·S_C,tau²,tau³route throughdense_matmul_real. All real-typed (octonion regular rep + so(8) adjoint), so no dtype change.- 3 docstring
@→·— the prosetau = S_B @ S_Creferences (regex false-positives) reworded to·, since they are not numpy math.
numpy-math ratchet matmul 115 → 105. Pure Python-tier; no C change, ABI stays 3. The triality parity tests pass unchanged (tau³ = I₂₈, Fix(tau) = g₂ exactly at dim 14, the order-3 outer-automorphism structure). qm.so8's ~17 real sites + the Minkowski / DSP real sites land in subsequent batches.
SSoT: issue #928; the numpy-math ratchet (test_numpy_math_ratchet.py).
[0.7.5rc21] - 2026-06-08¶
The matmul-kernel phase, batch 6 — the real-linear-algebra cascade trio + hypercomplex_dft octonion-rep matvecs (numpy-math matmul 123 → 115; #928). The complex contraction surface was closed at rc20; the remaining ~70 sites are real-typed. This batch introduces the real cascade primitives and migrates the first uniform cluster.
- New
srmech.amsc.laplacian.dense_matmul_real/dense_matvec_real/dense_dot_real— float64 peers of the complex helpers. Each routes the real contraction through the native complex kernel (on imag-free input) and drops the exactly-zero imaginary part, so real-typed sites leave numpy@/.dotfor a cascade without a dtype change. All three arecomposition_of_cin the Rosetta ledger (no own C symbol; the math rides thec_dispatchedcomplex kernel; standalone-ready).dense_matmul_real/dense_dot_realget their first callsites in the next batches (so8 / triality). amsc.cascade.hypercomplex_dft(8 sites) — the octonion regular-representation matvecs (octonion_left/right_mult(w) @ vec, 8×8 real × real-8) in the QDFT/ODFT core +hypercomplex_couplenow route throughdense_matvec_real. The F378 non-associativity bracketing is preserved exactly. Imported lazily inside the functions (after the numpy guard) so the cascade layer stays numpy-absent-safe (§22).
numpy-math ratchet matmul 123 → 115. Pure Python-tier; no C change, ABI stays 3. QDFT/ODFT parity tests + the numpy-free import tests pass unchanged. The remaining real so8 / triality / Minkowski / DSP sites land in subsequent batches.
SSoT: issue #928; the numpy-math ratchet (test_numpy_math_ratchet.py).
[0.7.5rc20] - 2026-06-08¶
The matmul-kernel phase, batch 5 — the complex vecmat/dot/sandwich sites onto a new dense_dot_complex bilinear helper (numpy-math matmul 135 → 123; #928). The complex 2-D matmul surface was exhausted at rc19; this batch closes the genuinely-complex contraction sites by composing the existing dense_matvec_complex with a new bilinear inner-product helper.
- New
srmech.amsc.laplacian.dense_dot_complex(a, b)— the plain bilinearΣ aᵢ bᵢ(matching numpya·bon two 1-D arrays, NOT the conjugatingvdot). Composes the native-dispatchedelementwise_multiply_complexcascade with a reduction sum — never a numpy contraction operator. Classifiedcomposition_of_cin the Rosetta ledger (standalone-ready; not a debt bucket). Callers spell the Hermitian form asdense_dot_complex(a.conj(), …). qm.pseudo_hermitian(3 sites, 7@tokens) — the η-sandwiches⟨a|η|b⟩,⟨ψ|ηO|ψ⟩,⟨ψ|η|ψ⟩now route each contraction throughdense_matvec_complex+dense_dot_complex.signal_processing.heat_kernel(2) — the eigenbasis project/reconstruct matvecs →dense_matvec_complex.spectral(2) — the decompose/recompose eigenbasis matvecs →dense_matvec_complex.signal_processing.music(1) — the noise-subspace projectionEnᴴ·A(a complex 2-D matmul) →dense_matmul_complex.
numpy-math ratchet matmul 135 → 123. Pure Python-tier; no C change, ABI stays 3. Parity tests pass unchanged (pseudo-Hermiticity + η-expectation; heat-kernel diffusion; spectral round-trip; MUSIC pseudo-spectrum). The remaining real-typed sites (so8 / triality / octonion-DFT / Minkowski / DSP) await a real-matmul + real-matvec cascade; the matrix_cascades QR-internal vdot/back-solves await a shape-polymorphic pass.
SSoT: issue #928; the numpy-math ratchet (test_numpy_math_ratchet.py).
[0.7.5rc19] - 2026-06-08¶
The matmul-kernel phase, batch 4 — qm.relativistic γ-products + qm.pseudo_hermitian η-products onto the cascade (numpy-math matmul 147 → 135; #928). 12 dense complex matmuls route through dense_matmul_complex:
qm.relativistic(9) — the Dirac γ-matrix products:γ_5 = i·γ0γ1γ2γ3, the Clifford anticommutator{γ^μ,γ^ν},γ_5², the{γ_5,γ^μ}chirality anticommutator, and the charge-conjugationC = iγ2γ0.qm.pseudo_hermitian(3) — the pseudo-Hermiticity residualOᴴη − ηOand the metric buildη = (V·Vᴴ)⁻¹.
numpy-math ratchet matmul 147 → 135. No Rosetta bucket move. No behaviour change: the relativistic + pseudo_hermitian parity tests pass unchanged (Cl(1,3) Clifford residuals + γ_5 relations at machine precision; pseudo-Hermiticity + η-construction hold). Pure Python-tier; no C change, ABI stays 3.
Deferred (need new primitives): qm.relativistic's eta@k Minkowski matvec/dot and qm.pseudo_hermitian's vᴴηv / vᴴηOv eta-sandwich vecmat-dot sites — these want a real-matmul cascade and a vecmat/dot helper, the subject of a later batch. qm.triality is entirely real-typed (octonion-rep matrices) and also awaits the real-matmul variant.
SSoT: issue #928; the numpy-math ratchet (test_numpy_math_ratchet.py).
[0.7.5rc18] - 2026-06-08¶
The matmul-kernel phase, batch 3 — qm.spin + qm.gauge Lie-algebra products onto the cascade (numpy-math matmul 168 → 147; #928). Two QM modules' dense complex matmuls now route through dense_matmul_complex instead of numpy @:
qm.spin(15) — every Pauli-matrix product inpauli_clifford_residuals: the anticommutator residuals{σᵢ,σⱼ}/σᵢ²−Iand the cyclic commutator residuals[σᵢ,σⱼ]−2iσₖ.qm.gauge(6) — the SU(N) Lie-algebra surface: the structure-constant commutator[Tᵃ,Tᵇ], the quadratic CasimirΣTᵃTᵃ, the segment-holonomyexp(M)=V·diag(eⁱᵠ)·Vᴴ, and the path-ordered Wilson-loop product.
numpy-math ratchet matmul 168 → 147 (21 callsites). Both modules are now numpy-@-free. No Rosetta bucket move — these are diagnostic / algebra helpers already composing the Class-L primitives. No behaviour change: the spin + gauge parity tests pass unchanged (the Clifford/commutator residuals stay at machine precision; SU(2)/SU(3) Casimir eigenvalues and Wilson-loop unitarity hold). Pure Python-tier; no C change, ABI stays 3.
SSoT: issue #928; the numpy-math ratchet (test_numpy_math_ratchet.py).
[0.7.5rc17] - 2026-06-08¶
The matmul-kernel phase, batch 2 — qm.single_particle contractions onto the cascade (numpy-math matmul 180 → 168; #928). The 12 dense complex contractions in qm/single_particle.py now route through the dense_matmul_complex / dense_matvec_complex cascades instead of numpy @:
commutator[A,B] = AB − BA— two dense matmuls.heisenberg_evolveA_H = Uᴴ·A·Uandliouville_evolveρ(t) = U·ρ·Uᴴ— theU = V·diag(phases)·Vᴴbuild + the conjugation, all through the matmul cascade.tdse_evolvethe eigenbasis changeVᴴ·ψ/V·ψ'— through the matvec cascade.
After this, the module's only matmul-category callsite is the np.outer rank-1 product in density_matrix (a distinct op, no matmul kernel involved). numpy-math ratchet matmul 180 → 168. No Rosetta bucket move — these ops were already composition_of_c (they compose the hermitian_eigendecompose Class-L cascade). No behaviour change: the 27 single_particle parity tests pass unchanged (the QM operator algebra holds to the same round-off; the native triple-accumulator differs from BLAS only at the last bits). Pure Python-tier; no C change, ABI stays 3.
SSoT: issue #928; the numpy-math ratchet (test_numpy_math_ratchet.py).
[0.7.5rc16] - 2026-06-08¶
The matmul-kernel phase, batch 1 — matrix_cascades dense matmuls onto the cascade (numpy-math ratchet matmul 185 → 180; #928). First @-callsite migration against the rc14 dense_matmul_complex C kernel. The 5 dense complex 2-D matmuls inside matrix_cascades.py — the AᴴA / A·V / AAᴴ / Aᴴ·U Gram + reconstruction products of svd, and the R·Q shifted-QR step of eigvals (which qr/lstsq ride) — now route through laplacian.dense_matmul_complex instead of numpy @, so numpy stays carriers-only in the matrix-decomposition cascades.
numpy-mathratchetmatmulceiling 185 → 180 (5 callsites migrated). Theqr/svd/lstsq/eigvalsops were alreadycomposition_of_c(they compose thehermitian_eigendecomposeClass-L cascade); routing their internal contraction through the matmul cascade keeps that and removes the last numpy-@math from the SVD/eig Gram-matrix path.- No behaviour change: the 25
matrix_cascades/lstsq-einsum-eig/numpy-optionalparity tests pass unchanged (the decomposition invariants — reconstruction, orthonormality, descending singular values — hold to the same round-off; the native triple-accumulator differs from BLAS only at the last bits, well inside the existing tolerances). ABI unchanged (no C change this rc — the kernel shipped in rc14).
Pure Python-tier migration; no C change, ABI stays 3. SSoT: issue #928; the numpy-math ratchet (test_numpy_math_ratchet.py).
[0.7.5rc15] - 2026-06-08¶
Bug fix — cascade.kuramoto_step(adjacency=...) now honors the coupling scalar (UPSTREAM_NOTES §32; PR#687 F636). When an adjacency= matrix was passed, the global coupling (K) was dropped on the floor: coupling=0.0 and coupling=3.0 produced bit-identical trajectories over the same neighbor graph (only the all-to-all adjacency=None path scaled by K correctly). The adjacency branch built its weight from the matrix alone, behaving as if coupling==1.0 regardless.
- Fix (Python + C, co-equal): the adjacency-branch weight is now
coupling · A[i][j]— the globalKSCALES the matrix, matching the all-to-all branch'sK/N.coupling=0zeroes the coupling term;couplingtunes global strength.srmech_kuramoto__general_sum(insrmech_kuramoto.c) gains acoupling_kparameter;compose.kuramoto_stepmultiplies in the Python fallback. No behaviour change at the defaultcoupling=1.0(whereK·A == A) — which is exactly why every prior adjacency test missed it. - Regression tests: new
test_adjacency_*cases exercisecoupling≠1on the adjacency path (the bug surface):coupling=0over a ring → pure drift;coupling=3vscoupling=0over the same ring diverge; a full1/nmatrix atcoupling=Kreproduces the all-to-allKpath. The_reference_generaltest spec was corrected to scale bycoupling(it carried the same masked bug). Differential-tested against a freshly-built native lib; the native parity test FAILS against the pre-fix C and PASSES against the fixed C.
Bug fix only; additive C parameter on a static helper (ABI stays 3), no public signature change. SSoT: docs/srmech/rbs_lm_research/UPSTREAM_NOTES.md §32.
[0.7.5rc14] - 2026-06-08¶
The matmul-kernel phase opens — a native dense complex matmul C kernel (#928). First kernel of the new-C-kernel phase that drives the numpy-math ratchet (and the Rosetta python_only_debt cluster) toward zero. Dense complex matmul is the top single lever — it's the contraction the QM / matrix_cascades layer's @ math is built on.
- New C symbol
srmech_dense_matmul_complex((m,k)·(k,n)=(m,n), interleaved real/imag, bounded ≤ 256 per dim) — a JPL-clean triple-accumulator mirroringsrmech_dense_matvec_complex. Additive symbol; ABI stays 3 (hasattr-guarded bind). laplacian.dense_matmul_complex(A, B)— the Class-L contraction the matmul math will route through, so numpy stays carriers-only. Nativesrmech_dense_matmul_complexwhen present; the no-native fallback composes thedense_matvec_complexcascade column-by-column — itself a cascade, never numpy@. Registered in the tool schema (describetotal 255 → 256) and classifiedc_dispatched(Rosetta inventory 348 → 349).- This rc ships and proves the kernel (parity test + CI build); the QM /
matrix_cascades@-callsite migrations that decrement the ratchet'smatmulceiling (185) are the next batches against this now-published kernel.
New additive C kernel + Python op + parity test; no behaviour change to existing ops, ABI stays 3. SSoT: issue #928; c/ROSETTA_LEDGER.md.
[0.7.5rc13] - 2026-06-08¶
numpy-math ratchet + lmmse → cascade (the "numpy is a carrier, not a math engine" guard; #928). A new down-only source-level guard — sibling of the libm C-transpile ratchet (which drove libsrmech 23 → 0) and the Rosetta ratchet — that keeps all math in srmech cascades and numpy in vector-packing only. numpy bundles a full math engine (np.linalg.*, np.fft.*, the @ matmul, the transcendental ufuncs) alongside its carrier; every one of those is libm-at-the-array-level with a srmech cascade equivalent, so a stray np.linalg.solve is a defect, not a convenience.
- The ratchet (
tests/test_numpy_math_ratchet.py). Greps the srmech source (not the tests) for numpy-math callsites in three categories and pins each at a TIGHT, down-only ceiling:linalg_fft126 ·matmul185 ·ufunc48 (np.{sin,cos,exp,sqrt,sign,…}). A newnp.linalg.*now fails CI; closing debt = route the callsite through the cascade that backs it, then lower the matching ceiling. Carrier ops (zeros/asarray/reshape/.T/elementwise+ - */indexing) are NOT counted; reductions (sum/mean/…) are a deferred boundary category. Version-stable across CPython 3.10–3.14 (regex over source, nottokenize). - Decrement #1 —
lmmse.signal_processing.closed_form_ops.lmmserouted its gain offnp.linalg.solve+ its estimate off the@matmul. Both now ride srmech cascades: the solve throughlaplacian.dense_solve(nativesrmech_dense_solve_f64/ exact-rational Gauss-Jordan), the matvec throughlaplacian.dense_matvec_complex. numpy is carriers-only there now (asarray/transpose/elementwise ±). The estimate is correct to machine precision (gain-equation residual‖K·R_yy − R_xy‖ ≈ 4e-16); the cascade — not LAPACK — is the source of truth. - Rosetta ratchet:
lmmse.opreclassifiesc_exists_unbound → composition_of_c(it now composes twoc_dispatchedops). Ceilingc_exists_unbound6 → 5;python_only_debtunchanged at 108. Total standalone-C debt 114 → 113. The remaining 5c_exists_unboundare the Klein-4 family (gated on W5).
Behaviour-preserving (machine-precision) dispatch retrofit + a new source guard; no new op, ABI stays 3. SSoT: issue #928; c/ROSETTA_LEDGER.md. User direction 2026-06-08: "numpy is only ever for vector packing."
[0.7.5rc12] - 2026-06-08¶
Rosetta cheap-win sweep #5 — the polar-HDC trio goes native (#928). The three srmech.amsc.hdc polar Class-M ops that ship a bit-exact C twin but built the result in pure numpy now dispatch to native when present: polar_bind → srmech_polar_bind (int8 element-wise sign-product, 0 absorbing), polar_bundle → srmech_polar_bundle (per-position sticky-majority sign(Σ), tie → 0), polar_density → srmech_polar_density (informative-fraction = count-nonzero / n). The pure-Python numpy paths stay as the Pyodide / no-native fallback.
- Bit-exact — all three are integer int8 ops (the density is an integer count over one division); verified native-vs-pure agree over 200 random trials each, so outputs are unchanged.
- Reclassify:
polar_bind,polar_bundle,polar_densityc_exists_unbound → c_dispatched. - Ratchet: ceiling
c_exists_unbound9 → 6;python_only_debtunchanged at 108. Total standalone-C debt 117 → 114. - Last clean cheap-win. The remaining 6
c_exists_unboundare NOT clean bit-exact wins:lmmse(1) would routenp.linalg.solveto the Csrmech_dense_solve_f64, but that twin is NOT bit-exact with LAPACK (agrees to ~2.2e-16, a separate LU) — a deliberate float-boundary decision, held; and the Klein-4 family (5) stays gated on W5 (klein4_bundleeven-count) per[[feedback_check_known_bugs_before_mirroring_python_to_c]]. Both warrant a maintainer call before wiring.
Behaviour-preserving dispatch retrofit + docs; no new op, ABI stays 3. SSoT: issue #928; c/ROSETTA_LEDGER.md.
[0.7.5rc11] - 2026-06-08¶
Rosetta cheap-win sweep #4 — the Hamming GF(2) block code goes native (#928). The two srmech.amsc.cascade Hamming primitives that ship a bit-exact C twin (the v0.7.2rc2 srmech_hamming_* pair) but built the codeword / syndrome in pure Python now dispatch to native when present: hamming_encode → srmech_hamming_encode (the systematic GF(2) generator) and hamming_syndrome → srmech_hamming_syndrome (the parity-check error position). The pure-Python GF(2) loops stay as the Pyodide / no-native fallback. With hamming_syndrome C-backed, hamming_decode_correct (which calls it, then single-bit-corrects) is now a composition of a C-dispatched twin.
- Bit-exact — GF(2) integer arithmetic, no float; verified native-vs-pure agree across
n ∈ {2,3,4}and every single-bit-flip codeword, so encode/syndrome/decode outputs are unchanged. - Reclassify:
hamming_encode,hamming_syndromec_exists_unbound → c_dispatched;hamming_decode_correctc_exists_unbound → composition_of_c. - Ratchet: ceiling
c_exists_unbound12 → 9;python_only_debtunchanged at 108. Total standalone-C debt 120 → 117. The remaining 9c_exists_unboundare the polar-HDC trio +lmmse(the rc12 batch) and the Klein-4 family (5; gated on W5 — theklein4_bundleeven-count semantics must be confirmed before its standalone-C sector-dispatch port per[[feedback_check_known_bugs_before_mirroring_python_to_c]]).
Behaviour-preserving dispatch retrofit + docs; no new op, ABI stays 3. SSoT: issue #928; c/ROSETTA_LEDGER.md.
[0.7.5rc10] - 2026-06-08¶
Rosetta cheap-win sweep #3 — the Cayley-Dickson basis cocycle goes native (#928). cascade.cd_basis_product (the integer cocycle e_i·e_j = sign·e_{i⊕j}) now dispatches to the C peer srmech_cd_basis_product when native is present (the symbol was bound but never called); the pure-Python iterative doubling stays as the Pyodide / no-native fallback. With that primitive C-backed, qm.octonion.octonion_mult_table now builds the (8,8,8) structure tensor through cd_basis_product instead of a recursive numpy _cd_mul.
- Integer-only ⇒ bit-exactness is trivial; the table's int8 bytes are identical, so
octonion_table_attestation'sresponse_sha256content-address is unchanged (verified7f36461e…). The recursive_cd_mul/_cd_conjugatehelpers (used only to build the table) are removed; the convention stays specified by the module docstring +_DOUBLING_RULE/_CONJ_RULEbyte constants (theparser_rule_hash) + the C primitive. - Reclassify:
cd_basis_productc_exists_unbound → c_dispatched;octonion_mult_tablec_exists_unbound → composition_of_c. 142 cayley/octonion/so8/hurwitz/triality/sedenion/rosetta tests green. - Ratchet: ceiling
c_exists_unbound14 → 12;python_only_debtunchanged at 108. Total standalone-C debt 122 → 120.
Dispatch retrofit + docs; no new op, ABI stays 3. SSoT: issue #928; c/ROSETTA_LEDGER.md.
[0.7.5rc9] - 2026-06-08¶
Rosetta cheap-win sweep #2 — octonion L/R-multiply + conjugate → native dispatch (#928). The three srmech.qm.octonion ops that built the octonion left/right-multiplication operators via np.einsum over the structure-constant table (and the conjugate via slice-negate) now delegate to the already-C-dispatched hdc.loop_left_op / loop_right_op / loop_conj (the octonion loop family, srmech_loop_*_f64). Same Cayley-Dickson-from-H convention on both sides (verified bit-exact over 50 random trials + the byte-identical structure table), so output is unchanged and the so(8)/triality engine downstream is unaffected (118 qm tests green).
- Repointed:
octonion_left_mult,octonion_right_mult,octonion_conjugate— input validation preserved, compute delegated. These reclassifyc_exists_unbound → composition_of_c. - Ratchet moves down:
test_rosetta_completeness.pyceilingc_exists_unbound17 → 14;python_only_debtunchanged at 108. Total standalone-C debt 125 → 122. octonion_mult_tablestaysc_exists_unboundfor now — it composescascade.cd_basis_product, which must itself gain native dispatch first (the natural rc10 batch; the table is content-addressed byoctonion_table_attestation, so the bytes must stay identical — verified they do).
Behaviour-preserving dispatch retrofit + docs; no new op, ABI stays 3. SSoT: issue #928; c/ROSETTA_LEDGER.md.
[0.7.5rc8] - 2026-06-08¶
Rosetta cheap-win sweep #1 — SHA-256 mint cluster routed onto native dispatch (#928; the first c_exists_unbound close). The 6 srmech.signal_processing ops that minted HDC vectors / content strides via raw hashlib.sha256(...).digest() now route through srmech.amsc.format.sha256_raw, picking up the native C SHA-256 (incl. the SHA-NI / AVX2 tiers) transparently. This closes a Rosetta debt and a standing CLAUDE.md discipline violation (no raw hashlib.sha256 callsites) in one sweep. Bit-identical — sha256_raw(x) == hashlib.sha256(x).digest() — so every mint output is unchanged (29 rbs-lm behaviour tests green).
- Repointed:
mint_vector,mint_class_operator,mint_cascade_composition,mint_stance_fingerprint,encode_loe_content(rbs_hdc_instrument.py) +compute_content_stride(form_function_rotation.py). The deadsha256_bytesimport inrbs_hdc_instrument(imported, never used while the mint path bypassed it onto rawhashlib) is replaced with the now-usedsha256_raw;import hashlibdropped from both modules. - Ratchet moves down: these 6 reclassify
c_exists_unbound → composition_of_c(they now compose the C-dispatchedsha256_raw+hdc.*).test_rosetta_completeness.pyceilingc_exists_unbound23 → 17;python_only_debtunchanged at 108. Total standalone-C debt 131 → 125.
Docs/tooling + a behaviour-preserving dispatch retrofit; no new op, no ABI change (ABI stays 3). SSoT: issue #928; c/ROSETTA_LEDGER.md. [[feedback_check_known_bugs_before_mirroring_python_to_c]] (W4-aware: raw-digest sites use sha256_raw, not the hex sha256_bytes).
[0.7.5rc7] - 2026-06-08¶
Rosetta-completeness AUDIT + down-only ratchet (#928). Stands up the measurement instrument for the C-mirror goal — every public compute op should dispatch to a bit-exact C twin OR be a pure composition of such twins, so libsrmech runs standalone (full OS or thread-less microcontroller, no host Python). Docs + tooling only; no runtime/ABI change (ABI stays 3).
- The audit. All 348 public ops across
srmech.amsc/srmech.qm/srmech.signal_processingwere enumerated and classified — by reading each implementation against the exported C-symbol surface — into six buckets. Committed SSoT:python/tests/rosetta_classification.ndjson(regenerable vianotes/_rosetta_inventory.py→notes/_rosetta_build_classification.py). Baseline: 78c_dispatched· 61composition_of_c· 22bignum_reference(oracle tier) · 56non_compute· 23c_exists_unbound(cheap debt) · 108python_only_debt(debt). Total standalone-C debt = 131. - The ratchet (
python/tests/test_rosetta_completeness.py). Enumerates the live public surface, asserts it agrees EXACTLY with the committed classification (a new op with no bucket fails → forces every addition to be classified; a removed op left in the file fails → keeps the ledger current), and pins the two debt buckets at down-only ceilings (python_only_debt ≤ 108,c_exists_unbound ≤ 23) — raising a ceiling is the one edit the test exists to forbid. - The leverage map (rc8+ work-list). The 108 irreducible cluster on a handful of missing C kernels — dense complex
matmul(~15 qm ops), FFT/DFT (~20 sp+cascade), generaleig/SVD/QR/lstsq(~16 so8/triality/matrix_cascades),kron(~6 bell/so8),einsum/convolve/correlate(~8). The 23 cheap wins are wire-up only (the C twin already ships): Klein-4/polar HDC (8; klein4 gated on W5), octonion einsum→loop_*_op(4), Hamming (3), the SHA-256 mint cluster (6; also a CLAUDE.mdhashlib.sha256-direct-call discipline fix),cd_basis_product(1),lmmse→dense_solve(1). Full breakdown inc/ROSETTA_LEDGER.md.
SSoT: issue #928; c/ROSETTA_LEDGER.md (measured baseline + leverage table). [[feedback_check_known_bugs_before_mirroring_python_to_c]].
[0.7.5rc6] - 2026-06-08¶
Coupled-wave (EM quadrature) driver + multi-stream multiplex — the sentence-structure arc's named ops (#928 W17/W18; F573/F577). Two new public cascade ops; pure-Python composition of existing C-backed primitives (calculus.{sin,cos} + Class-K pin_slot_at_zero + Class-M hdc), so no new primitive class, no new C kernel, ABI stays 3:
cascade.coupled_wave(theta, *, handedness=+1, components=("sin","cos"))(W17 / F577). The full-chirality(E, B)drive instead of a collapsed 1-bit sign — a flatsign(wave)gate flips hard at every zero-crossing (2/cycle, the "verb-flip" structure error); the coupledE=sin, B=cos(90° apart, exactly EM) rotates monotonically → 0 hard reversals, so a driven chiral element keeps a stable bearing. Returns(E, B, handedness, klein4_quadrant)— the four(sign E, sign B)quadrants ARE the four Klein-4 (γ₅, iω₇) sectorsparallel_sector_dispatchuses. Handedness is a settable convention, never hardcoded — left/right are both first-class (the endianness posture; the substrate privileges neither byte-order nor chirality), related by a Class-K phase sign-flipθ → −θ(noabs), and the chosen sense is echoed back stable (it does not flip with θ).[[feedback_chirality_convention_is_settable_like_endianness]].cascade.multiplex_streams(streams, *, mode="roundrobin", roles=None)(W18 / F573-F577). Recombine N steering waves into one driver. A stream is a per-step real-valued driver wave (a steering signal that decides which content is selected downstream), not tokens — the output is a single steering driver; emission is a SEPARATE downstream consumer (the layer boundary is kept). Ideally each stream is acoupled_wavebearing (W17+W18 compose). Modes:roundrobin(default; the validated-bestt mod Nmultiplex),superpose(real-field interference = elementwise SUM + renormalise by max magnitude — like summing E-fields; nothdc.bundle, which is a different content-vector layer),pickbest(strongest-bearing wave per step, Class-K magnitude).roles=("S","V","O")binds each stream to a clause slot (verb stream a coupled bearing so its which-way can't flip mid-clause), tagged via Class-Mhdc.bindfor unbindable storage.- Registered in the tool schema (
describetotal 253 → 255); coverage + count-pin tests updated; newtests/test_coupled_wave.py(13). The DSL cascade-catalog.tomldescriptors (forsrmech dslchainability) are an optional follow-up —coupled_waveis a generator (not a unary chain stage). The W18 wave/HV data model + the handedness convention are flagged in the PR for the parallel sentence-structure session (PR #687) to confirm.
SSoT: PR #687 rbs_lm_research/SRMECH_BUGFIX_WISHLIST.md (W17/W18 long-form); [[user_stance_epicycle_via_gear_plus_pin]] + [[feedback_sign_handling_is_class_k_pin_slot_not_alu_abs]].
[0.7.5rc5] - 2026-06-08¶
PAL stream IPC + srmech_bus.c retrofit — the last raw-OS surface becomes #ifdef-free. rc4 born the PAL with a thread surface; this rc gives it the stream-IPC surface and retrofits the bus onto it, so the cross-process IPC peer carries zero #ifdef _WIN32. Additive C only; no new primitive class, ABI stays 3 (the PAL symbols are internal cross-TU, like the HAL's; the six srmech_bus_* public symbols + the handler typedef are byte-unchanged):
srmech_plat_stream_*(new PAL surface):listen/accept(single-accept;srmech_bus_server_accept_oneis public) /server_close/connect/read_exact/write_all/conn_close, plussrmech_plat_has_streams(). The endpoint-name → OS-path mapping moves entirely into the PAL — POSIX$HOME/.srmech/bus-<name>.sock(AF_UNIX socket) vs Windows\\.\pipe\srmech-<name>(named pipe). Opaque server+conn handles (max-aligned, no heap) mirror the rc4 thread-handle pattern. Carries forward the load-bearing no-FlushFileBuffers-on-Windows-pipes invariant (it deadlocks the request-reply pattern). Bare-metal target: a stream-less stub sosrmech.buscleanly reports unavailable.srmech_bus.cretrofitted: the AF_UNIX-vs-named-pipe duality (path builders,read/write-exact,listen/accept/connect/close— ~16 platform branches across 4 helpers) collapsed onto the PAL. The 4-byte big-endian length framing + handler dispatch (the OS-agnostic parts) stay here; everything OS-specific is gone tosrmech_platform.c.srmech_bus.cnow has zero real platform conditionals. (The five removedread/write-fd/handle +ensure_dirhelpers left the JPL Rule-5 exempt list — the ratchet went down five entries; their PAL replacements each carry ≥2 asserts.)- WSL2 build authority (verified): all 32
c/src/*.ccompile pedantic-Werrorclean;libsrmech.solinks; the standalone-C bus round-trip runs end-to-end —srmech_bus_serve→ a threadaccept_one→srmech_bus_connect→srmech_bus_send_recv→ echo handler returns the payload bit-exact over the AF_UNIX PAL path. JPL audit green;srmech_plat_has_streamsadded to the Rule-5 exempt list (a compile-time capability accessor, likesrmech_plat_has_threads).
Next: rc6+ port the qm.* C linear-algebra kernels (complex matmul / hermitian-eig / kron) as bit-exact Rosetta twins, ticking python_only_debt toward 0; the test_rosetta_completeness.py ratchet lands alongside the first qm.* port (when there is a debt count to ratchet). Per the do-not-mirror gate (#928), W5 must be confirmed before the klein4 standalone-C port. SSoT: c/src/srmech_platform.{h,c}; c/ROSETTA_LEDGER.md; [[feedback_simd_optimize_path_goes_through_hal]].
[0.7.5rc4] - 2026-06-08¶
Platform Abstraction Layer (PAL) — the OS sibling of the SIMD HAL — opening the Rosetta-completeness arc (a complete C mirror of the Python surface, so the C partition runs standalone). Additive C only; no new primitive class, ABI stays 3:
c/src/srmech_platform.{h,c}(the PAL): the single compilation unit where OS-specific code lives, exactly asc/src/srmech_simd.{h,c}(the HAL) is the single place for CPU-specific code. In the project's framing the OS is part of the hardware the binary runs on, so the PAL is a second "hardware" abstraction sibling of the HAL ([[feedback_simd_optimize_path_goes_through_hal]], generalised from the CPU to the OS). First surface: threads —srmech_plat_thread_spawn/join+srmech_plat_has_threads(), hiding the POSIX (pthread) / Windows (CreateThread) / thread-less split behind an agnosticvoid(void*)job API (no heap — handle lives in caller storage). JPL-clean.srmech_parallel.cretrofitted onto the PAL: the Klein-4 four-sector dispatch now carries ZERO#ifdef _WIN32— it calls the agnostic PAL and chooses threaded-vs-serial at runtime viasrmech_plat_has_threads(). The serial path is always compiled (the bit-exact reference + the thread-less-target capability). Behaviour-preserving: same four sector duals, same bit-exact serial==threaded contract.- WSL2 Linux build authority: the full surface builds clean on Linux (
gcc/cmake, pedantic-Werror) — the canonical standalone-C build/test loop, verified:libsrmech.solinks, the PAL-threaded dispatch runs and returns the correct Klein-4 sectors. The cross-OS CI matrix (Linux/macOS/Windows) remains the gate. c/ROSETTA_LEDGER.md: the down-only debt ledger for the arc — classifies every public Python op asc_dispatched/c_exists_unbound/composition_of_c/python_only_debt, with the last (the debt: mostly theqm.*linear-algebra layer) driven toward 0. Bignum exact-rational references are a separate intentional tier, not debt.
Next: rc5 PAL stream/IPC + srmech_bus.c retrofit (the last raw-OS surface) + the test_rosetta_completeness.py ratchet; rc6+ port the qm.* C kernels. SSoT: c/src/srmech_simd.{h,c} (the HAL precedent); [[feedback_simd_optimize_path_goes_through_hal]].
[0.7.5rc3] - 2026-06-08¶
Completes the v0.7.0 C-transpile for the aperiodic transcendentals — rational.{exp,log,sqrt} now compute the SAME algorithm as the native peers and DISPATCH to them (the rc2 trig sibling, now for exp/log/sqrt). Pure-Python + Python-binding only; no C source change, no new primitive class, ABI stays 3:
exp— unified on the Cody-Waite ln2 reduction (x = n·ln2 + r,|r| <= ln2/2;exp(r)the Q61 integer Taylor;2^nfolded into the IEEE exponent). This replaces the old argument-halving-and-square reduction, which amplified error to ~345 ULP — the new path holds ~1 ULP AND is bit-exact withsrmech_exp. (Measured: oldrational.expvs libm = 7.7e-14; new = 2.2e-16.) The Rosetta discipline surfaced this: the C translation was more correct than the human-gated Python source, because the better ln2 reduction lived only in the C + notebook.log— NEW. The notebook listedrational.logassrmech_log's Python peer (a Rosetta pair) but the Python half was missing. Added it:x = m·2^efrom the bit pattern,mfolded into[1/√2, √2),log(m) = 2·atanh((m−1)/(m+1))the Q61 series,e·ln2recombined with the two-word ln2. Bit-exact withsrmech_log. Domain (matching the C):x < 0 → NaN,x == 0 → −Inf.sqrt— default unified on the C K=27 IEEE-bit cascade (x = M·2^e,root = isqrt(M << 2K)wheremath.isqrt== the C two-limbisqrt128, projected by2^(e/2 − K)), bit-exact withsrmech_rational_sqrt. Theprecision_bits=Nargument now selects the higher-precision bignum reference (the prior default);hypotthreads it through. A handful of mathematical-constant call-sites that need correctly-rounded values (e.g. the Bell/CHSH Tsirelson bound2√2) now passprecision_bits=64to use the reference path.- All three dispatch to C when
_native.has_native_explog()/has_native_sqrt(); the pure-Python Q61 is the bit-identical fallback.exp/log/sqrt/hypotnon-finite + domain cases are guarded in both paths so native and non-native agree.
Verified (clean, native): C↔Q61 0 mismatches over 40 000 each for exp, log, sqrt; vs libm exp 2.2e-16 / log 3.6e-15 / sqrt 2.2e-16. describe() total 252 → 253 (the new rational.log ToolEntry). New tests/test_explog_sqrt_q61_parity.py (11). Notebook §exp reconciled (halving → Cody-Waite) + rational.log now exists as the listed peer. SSoT: c/src/srmech_explog.c (rc46; fdlibm two-word ln2) + c/src/srmech_sqrt.c (rc45); [[feedback_continuous_number_line_pedagogical_obstacle]]; [[user_stance_bit_exact_means_not_projection_diagnostic]].
[0.7.5rc2] - 2026-06-08¶
Completes the v0.7.0 "C-transpile triality coherence" for trig — the Python float rational.{sin,cos,tan,atan,atan2} now compute the SAME Q61 fixed-point Class-N cascade as the native peer c/src/srmech_trig.c, and DISPATCH to it. rc42–rc46 built the C cascade (srmech_sin/cos/atan/atan2, Q61 integer Taylor, no libm) and routed the C-internal callers onto it — but the Python float trig still ran an exact-rational bignum series and never called the compiled C. Two renderings, two different integer algorithms (Q61 vs arbitrary-denominator bignum), same name → not bit-exact. This rc makes them one cascade. Pure-Python + Python-binding only; no C source change, no new primitive class, ABI stays 3:
- The Python float projection IS the Q61 cascade now.
rational.{sin,cos,atan,atan2}were ported line-for-line fromsrmech_trig.c: the Class-N Taylor series runs in Q61 fixed-point (denominator 2⁶¹ — a power-of-two Class-N rational), float appears ONLY at the finalfloat(v)/float(2**61)projection (the same two-step int64→double cast the C does). Python's arbitrary-precision ints reproduce the C int64/uint64 arithmetic exactly (& MASK64models C's uint64 wrap; an explicit truncate-toward-zero_q61_cdivmodels C's integer/). Result: the pure-Python path is bit-for-bit identical tosrmech_sinet al. (verified: 50 000 angles × {sin,cos,atan}, 0 mismatches). - The public functions dispatch to C when
_native.has_native_trig()— a transparent speedup (the per-call bignum series, incl. theatan2slow-path wart, is gone on a native install) that returns identical bits. When no native lib is present, the pure-Python Q61 is the bit-identical fallback. The exact-rational*_series_truncate(arbitrary-denominator bignum, where the W14 domain guards live) stays the separate higher-precision reference surface — it is intentionally not the float contract. - Non-finite reconciliation (a parity gap this surfaced):
srmech_sin(±∞)returns aBAD_INPUTstatus (writing NaN), so a raw dispatch would raise while the Python fallback returned NaN. A uniform guard now runs in both paths before dispatch —sin/cos(±∞)=NaN,atan(±∞)=±π/2(cascade π/2), and the standardatan2quadrant limits — so native and non-native agree across the whole domain incl. ±∞/NaN (verified: 30 000 × 4 ops, 0 mismatches; theatan2∞-grid matches libm). - The 7 C transcendentals (
srmech_{sin,cos,atan,atan2,exp,log,rational_sqrt}) are now bound in_native.py(ctypes argtypes/restype +sin_c/cos_c/atan_c/atan2_c/exp_c/log_c/rational_sqrt_cwrappers +has_native_trig()); the trig four are dispatched this rc, theexp/log/sqrtdispatch follows.
New tests/test_trig_q61_parity.py (11 tests): C↔Q61 bit-exact, native↔non-native bit-exact over the whole domain, and a libm correctness floor (~1 ULP) that holds with or without the native lib. describe() total unchanged. SSoT: c/src/srmech_trig.c (v0.7.0rc43, the Q61 constants from the Archimedes π-cascade); [[feedback_continuous_number_line_pedagogical_obstacle]] (the float is the projection-of-last-step); [[user_stance_bit_exact_means_not_projection_diagnostic]].
[0.7.5rc1] - 2026-06-07¶
Three still-open items from the RBS-LM srmech bug-fix wishlist (docs/srmech/rbs_lm_research/SRMECH_BUGFIX_WISHLIST.md; the list's top priorities W1/W2/W7/W10/W12 all already shipped across the rc18→0.7.4 arc). Pure-Python, additive — no new primitive class; ABI stays 3:
- W4 —
srmech.amsc.format.sha256_hex/sha256_raw(the naming companions ofsha256_bytes).sha256_bytesreturns a 64-char hex str — the_bytesnames the INPUT type, which trips callers who read it as a return type.sha256_hex(data)is the value-identical, name-says-return alias;sha256_raw(data)returns the raw 32-byte digest (soint.from_bytes(sha256_raw(d), "big")works directly instead ofint(sha256_bytes(d), 16)).sha256_rawisbytes.fromhex(sha256_bytes(...))— same native/stdlib dispatch, no newhashlib.sha256call site (Phase B5). - W14 —
atan_series_truncate/log1p_series_truncatenow refuse out-of-domain arguments loudly. The naive Taylor partial sums have radius of convergence 1; past it they silently returned a divergent rational (atan(2/1)→ ~3e16,log1p(799/1)→ blow-up). They now raise a Class-N domainValueErrorfor|x| > 1(atan) /x > 1orx ≤ -1(log1p) — the §15.1/§18 Class-K contract-error pattern (refuse, don't answer wrongly). The|x| = 1conditional boundary (atan(1) = π/4, log1p(1) = log 2) stays allowed. These exact-rational ops cannot range-reduce (π is irrational); the float projectionrational.atan(x)IS band-reduced and stays the path for|x| > 1. - W15 —
srmech.amsc.cascade.cayley_dickson.{closure, left_orbit, min_generating_set}— the combinatorial loop-navigation layer over the registeredcd_basis_productcocycle (the loop-shelf arc F541/F544/F546 re-derived these each time; now a named home).closure(dim, gens)→ the sub-loop a generator set spans (a single octonion unit → 4, all 7 → the full loop 16);left_orbit(dim, start, gen)→ one left-multiplication cycle;min_generating_set(dim, units)→ the loop's navigation dimensionality (3 for 𝕆, 2 for ℍ, 1 for ℂ). Pure-integer, numpy-free.
describe() total unchanged — the new functions are alias / companion / building-block helpers of already-registered entries, so they are tool-schema-coverage-exempt exactly like sha256_bytes's validate_mpr_record / write_ndjson and cd_basis_product's cd_add / cd_basis.
Plus two pre-existing sedenion_register fixes (folded in to keep the lean CI green — both shipped latent in v0.7.4; neither in this rc's W4/W14/W15 surface):
- Cascade-honesty:
SedenionRegister.read()usedabs()on similarities to track max-magnitude. Replaced with the explicit Class-K pin-slot sign-branch (s if s >= 0 else -s), bit-identical for every float, polarity carried separately as Class-Cbest_sign— per[[feedback_sign_handling_is_class_k_pin_slot_not_alu_abs]](closes thetest_no_abs_calls_anywhere_in_srmechratchet). - MCP resolution (a W1-class bug): the
srmech.amsc.cascade.sedenion_registerToolEntry did not resolve to a callable once the same-named submodule was imported (Python rebinds the package attribute from the re-exported factory to the module object). The dotted-name resolver now prefers the same-named callable inside a colliding module — the "module X re-exports callable X" convention — so the registry name resolves regardless of import order (closes the W7 ratchettest_schema_signature_alignment_no_drift).
SSoT: RBS-LM bug-fix wishlist (PR #687); Hurwitz (1898); Baez arXiv:math/0105155 §2; Apostol Mathematical Analysis 2nd ed. Theorem 12.20.
[0.7.4] - 2026-06-06¶
Production graduation of the rc1–rc2 arc — the sedenion-addressable RBS-HDC instrument + three RBS-LM candidate-additions (PR #687 UPSTREAM_NOTES §31 / §1.2 / §1.3 / rbs_nn Note 1). All compositions over the existing 14-class vocabulary — no new primitive class; ABI stays 3:
srmech.amsc.cascade.sedenion_register/SedenionRegister(rc1; §31) — the sedenion box made an addressable instrument: 16 named slots (octonion working blocke0..e7= the ≤7 reversible coupler word;e8..e15= the Hamming carry block), HDCwrite/read, and the genuinely-newnavigate(address↔Cayley–Dickson homomorphism) +is_navigablereversibility gate (single-basis always reversible; composite-direction reversible only ≤𝕆 — the Hurwitz horizon).cascade.signed_sum_squared(rc2; §1.2) — the bipolar coupling-score (Class K ∘ L; noabs()).cascade.top_k_by_score(rc2; §1.3) — catalog top/bottom-K selection (Class E ∘ K; stable on ties).hdc.bundle_with_ties(rc2; rbs_nn Note 1) — majority for any N with the Class-K bundle-tie (zero-crossing) surfaced; odd-N majority equalsbundleexactly.
describe() total 248 → 252 (1 + 3 ToolEntry). No code change from rc2 — version-string graduation only; the rc1+rc2 state was verified-green on TestPyPI (clean-venv outside the source tree: native ABI 3, sedenion read-back 8/8 + working-word bit-exact + navigate 8/8, and the three additions correct on the core numpy-free install). PyPI README refreshed to v0.7.4. This fully drains the actionable PR #687 upstream queue (§29/§30/§31 + §1.2/§1.3/Note 1 all delivered). SSoT: UPSTREAM_NOTES (RBS-LM, PR #687); Hurwitz (1898); Baez arXiv:math/0105155; Kanerva (2009).
[0.7.4rc2] - 2026-06-06¶
Three RBS-LM UPSTREAM_NOTES candidate-additions (#687 §1.2 / §1.3 / rbs_nn Note 1) — the still-open, non-stale gaps from the research branch, each a pure composition over the 14-class vocabulary (no new primitive class; ABI stays 3; all numpy-free):
srmech.amsc.cascade.signed_sum_squared(sources)(§1.2) — the coupling-score composite: per position,s = Σ_sources (2·bit−1)(Class K bipolar) thens²(Class L signed-magnitude-square). Large where sources agree, ~0 where they cancel; noabs().srmech.amsc.cascade.top_k_by_score(scores, k, *, largest=True)(§1.3) — the catalog selection composite: Class E (sorted-key order) ∘ Class K (sparse truncate to top/bottom k). Stable on ties. The band-selection / weak-coupling-prune step.srmech.amsc.hdc.bundle_with_ties(vectors)(rbs_nn Note 1) — bitwise majority across any N, returning(majority, ties): the tie bit marks where the bundle accumulator crosses zero — a Class K event (the phase-boundary / derivative-sign-flip of MFO §VII.6.12.1) — surfaced without changing the binary-byte storage form. For odd N the majority byte equalsbundleexactly.
describe() total 249 → 252 (3 ToolEntry). Re-scan of PR #687 confirmed §31 is the last section and §29/§30/§31 are all delivered; the remaining notes are stale (superseded by the v0.6.0/v0.7.0 arcs) or explicitly non-blockers. SSoT: UPSTREAM_NOTES §1.2/§1.3 (RBS-LM) + rbs_nn Note 1; Kanerva (2009) Hyperdimensional Computing.
[0.7.4rc1] - 2026-06-06¶
Sedenion-addressable hyper-loop RBS-HDC instrument (#687 UPSTREAM_NOTES §31; F465 + F468) — the sedenion box made into an RBS-HDC instrument. A composition over shipped v0.7.3 primitives (no new primitive class, no new algebra; ABI stays 3):
srmech.amsc.cascade.sedenion_register/SedenionRegister— the sedenion (dim-16) address space: 16 named slotse0..e15. The octonion blocke0..e7is the ≤7 reversible working word (hypercomplex_couple, bit-exact ≤𝕆);e8..e15is the EC/carry block (hamming_*, GF(2), §30). HDC ops instead of ALU:write/readare random-access-by-name (hdc.bind+ nearest-codebook clean = associative superposition, classical, no quantum cost);couple_working/uncouple_workingare the reversible word;carry/correctthe single-error-correcting EC block.- The genuinely-new surface — the operational hyper-loop (F468):
navigate(j)is the address↔Cayley–Dickson homomorphism — right-multiply every slot name bye_jso addressing respectse_i·e_j = ±e_k(thecd_basis_productcocycle);is_navigable(direction)is the reversibility gate (left_mult_is_invertible): single-basis navigation is always a signed permutation (reversible at every dim), composite-direction navigation is reversible only ≤𝕆 — a sedenion zero-divisor direction breaks it. The hyper-loop's horizon IS the Hurwitz wall. - Fences: the associative capacity is
D-bounded (HDC crosstalk), distinct from the reversible working set (≤7, the coupler) — kept distinct (F465); real-coefficient EC stays out (the §30 GF(2)-only fence); sign is Class C (chiral_flip), neverabs(). Storage + coupler are the scientific tier (numpy on call);navigate/is_navigable/carry/correctare numpy-free.
describe() total 248 → 249 (the sedenion_register factory). The class composes hdc.{bind,bundle,similarity} + hypercomplex_couple + hamming_* + cd_basis_product/left_mult_is_invertible + chiral_flip. SSoT: UPSTREAM §31 (RBS-LM, PR #687); Hurwitz (1898); Baez arXiv:math/0105155; Kanerva (2009) Hyperdimensional Computing.
[0.7.3] - 2026-06-06¶
Production graduation of the rc1 arc — the Cayley–Dickson open-exterior boundary-demonstrator (#915 / MFO §VII.6.23). One cascade-catalog addition, a composition over the existing 14-class A–N vocabulary (no new primitive class; ABI stays 3):
srmech.amsc.cascade.cayley_dickson— the deliberately non-reversible object on the far side of the Hurwitz wall (ℝ→ℂ→ℍ→𝕆→𝕊(16)→…), exact-rational + numpy-free:cd_mult/cd_conjugate/cd_norm_sq, the integer cocyclecd_basis_product(with the JPL-cleansrmech_cd_basis_productC peer),sedenion_zero_divisor_witness, and theleft_mult_kernel/left_mult_is_invertible"no backward direction" detector. The executable falsifier for §VII.6.23 — zero divisors first at dim 16, composition norm fails at 16, conjugation defined every rung while the inverse dies at the wall. NOT a substrate extension (noqm.*peer, no DSL wiring; the closed sim stays ≤𝕆).
describe() total 240 → 248 (8 ToolEntry). No code change from rc1 — version-string graduation only; the rc1 state was verified-green on TestPyPI (clean-venv outside the source tree: HAS_NATIVE=True, ABI 3, native C↔Python cocycle parity = 0 mismatches across dims 1–64, witness (e1+e10)(e4−e15)=0). PyPI README refreshed to v0.7.3 (numpy-optional install, the v0.7.x cascade families). SSoT: Hurwitz (1898); Schafer (1954) Amer. J. Math. 76; Moreno arXiv:q-alg/9710013; Baez arXiv:math/0105155 §2.
[0.7.3rc1] - 2026-06-06¶
Cayley–Dickson open-exterior boundary-demonstrator (#915 / MFO §VII.6.23). The deliberately non-reversible object on the far side of the Hurwitz wall: the generic doubling ℝ→ℂ→ℍ→𝕆→𝕊(16)→trigintaduonion(32)→… , exact-rational and numpy-free. the_one / hypercomplex_couple live in the reversible interior (≤𝕆); this exhibits the wall the closed simulation does not cross, converting §VII.6.23's open-exterior claims from literature-only (Moreno arXiv:q-alg/9710013) to own-code-attested ([[feedback_own_work_is_primary_attestation]]). It is NOT a substrate extension — no qm.* peer, no DSL wiring; past 𝕆 there is no division-algebra substrate to be native to.
- New
srmech.amsc.cascade.cayley_dickson—cd_mult/cd_conjugate/cd_norm_sq(exact-rational generic product, convention from Baez §2;x·x̄ = N(x)·1at every rung),cd_basis/cd_basis_product(the integer basis cocyclee_i·e_j = ±e_{i⊕j}),sedenion_zero_divisor_witness(a concrete dim-16x·y = 0, both nonzero, found from our own table), andleft_mult_kernel/left_mult_is_invertible(the mapu ↦ x·uhas a kernel ⟺ no inverse map — the "no backward direction to point" of §VII.6.23.4, exact-rational RREF). Composition of A–N: M (bilinear bind) ∘ C (conjugation-ordered cross terms) ∘ K (sign-flip; noabs()) + N (rational anchor) + L (kernel rank); no new primitive class. - Native C peer
srmech_cd_basis_product(c/src/srmech_cayley_dickson.c) — the integer cocycle, the single doubling-recursion unrolled to a bounded loop (no recursion; JPL Power-of-Ten clean —-Wall -Wextra -Wpedantic -Werror, no libm/malloc/goto, ≥2 asserts/fn). Attested bit-exact against the Pythoncd_basis_productacross dims 1…64 bytests/test_cascade_cayley_dickson_parity.py. The arbitrary-rational product stays Python by the same vendoring-scope decision that keeps TOML parsing in Python (no bignum rational in libsrmech). ABI-additive — SRMECH_ABI_VERSION stays 3 (new symbol + two macros, ctypeshasattr-guarded). - Attested facts (exact, re-runnable): zero divisors first appear at 16 and are absent at ≤8 (Hurwitz); the composition norm
N(x·y)=N(x)·N(y)holds for dims ≤ 8 and fails at 16; conjugation is defined at every rung while the product's inverse dies at the wall (§VII.6.23.3 "chirality persists, its reversing power does not").
describe() total 240 → 248 (8 ToolEntry). SSoT: Hurwitz (1898); Schafer (1954) Amer. J. Math. 76; Moreno arXiv:q-alg/9710013; Baez arXiv:math/0105155 §2.
[0.7.2] - 2026-06-06¶
Production graduation of the rc1–rc2 arc — the (σ,θ,μ) coupler + the Hamming/GF(2) front-loader. Two cascade-catalog additions, both composites over the existing 14-class A–N vocabulary (no new primitive class; ABI stays 3):
cascade.hypercomplex_couple(rc1; #908, F436/F437) — the bidirectional(σ,θ,μ)hypercomplex coupler (COUPLE): bind ≥3 streams into one quaternion/octonion + a joint coherence channel and unbind losslessly (the conjugate twiddle; reversible ≤ 𝕆 by Hurwitz). General/diagonal μ also extendedquaternion_dft/octonion_dft.cascade.hamming_encode/hamming_syndrome/hamming_decode_correct(rc2; #910, §30 / F442/F449) — the Hamming/GF(2) single-error-correcting block code over the 2ⁿ−1 ladder (CARRY/EC): the front-loader's other half, lean-ALU XOR-native, shipped as a Rosetta pair with the JPL-cleansrmech_hamming_*C peer (attested bit-exact).
describe() total 236 → 240. The shipped libsrmech holds no libm transcendental (the C-transpile triality ratchet baseline stays ZERO). No code change from rc2 — version-string graduation only; the rc1+rc2 state was verified-green on TestPyPI (clean-venv, incl. the fresh C↔Python Hamming parity = 0 mismatches). SSoT: Sangwine & Ell (2012) arXiv:1001.4379; Błaszczyk (2019) arXiv:1905.12631; Hurwitz (1898); Hamming (1950).
[0.7.2rc2] - 2026-06-06¶
Hamming / GF(2) linear block-code family — the CARRY/EC half of the sedenion front-loader (#910 / §30; findings F442 + F449). Where hypercomplex_couple (rc1) is COUPLE — bind ≤7 streams reversibly into an octonion (capped at 𝕆 by Hurwitz) — this rc ships CARRY: hold >7 data items + locate/correct an error in one structure, reversible past 𝕆 using the sedenion's CODE geometry (its Fano/PG structure), not its broken chirality. Shipped as a Rosetta pair: a pure-Python spec + a JPL-clean C peer, attested bit-exact.
- New cascade ops
hamming_encode(data_bits, n)/hamming_syndrome(codeword)/hamming_decode_correct(codeword)over the 2ⁿ−1 ladder (Hamming(7,4) / (15,11) / (31,26) / …). Single-error-correcting (minimum distance 3): every single-bit error in every position is located and corrected, and the payload recovers exactly. Canonical 1-indexed construction — parity bits at the power-of-two positions, the syndrome IS the flipped-bit position. A Hamming(15,11) carrier holds 11 data + 4 EC in one structure (1.57× the octonion's 7 reversible slots, F449); Hamming(7,4) IS the octonion's own Fano plane (F441). Lean-ALU XOR-native (GF(2) add = parity = XOR); no float, no libm, noabs(). Class B (structure framing) ∘ I (cyclic index arithmetic) ∘ A (content-addressed error locator). - Native C peer
srmech_hamming_{encode,syndrome,decode_correct}(c/src/srmech_hamming.c) — JPL Power-of-Ten clean (no goto/malloc, bounded loops, ≥2 asserts/fn,-Wall -Wextra -Wpedantic -Werror-clean), no libm (the C-transpile triality ratchet baseline stays ZERO). Attested bit-exact against the Python spec bytests/test_cascade_hamming_parity.py(encode vectors, syndrome, decode across the 2ⁿ−1 ladder). ABI-additive — SRMECH_ABI_VERSION stays 3 (new symbols + one macro, ctypeshasattr-guarded). - Fences (F449): the code carries the GF(2) sector/structure bits + EC; real-valued coefficients ride alongside (real-field EC = RS/BCH is a separate, larger construction). No multiplicative product — bind/couple stays
hypercomplex_couple's job (≤𝕆). Single-error correction per rung.
describe() total 237 → 240 (the three Hamming ops). SSoT: Hamming, R. W. (1950), Bell Syst. Tech. J. 29(2):147–160. Provenance: PR #687 §30 / F442 / F449.
[0.7.2rc1] - 2026-06-06¶
General/diagonal μ-axis for the QDFT/ODFT + a bidirectional (σ,θ,μ) hypercomplex coupler (#908 §29; findings F436/F437). The shipped quaternion_dft / octonion_dft exposed named single μ-axes only ('i'|'j'|'k'|'ijk'): a single axis carries N streams (round-trips) but does not couple them — perturbing the i-stream leaves the j,k spectra untouched (a complex FFT on the (1,μ) plane + an independent transform on the rest). This rc closes that gap.
mu_axis(and the ODFTtwo_sided_right_axis) now accept'diagonal'or a general unit pure-imaginary vector —'diagonal'is the equal-weight axis of the active algebra ((i+j+k)/√3for ℍ,(Σ_{n=1..7}eₙ)/√7for 𝕆). A diagonal μ couples all streams:μ·Σsₙeₙfolds them into the real/anchor channel as a joint coherence detector (F436: coherent streams add ∝ n·s, incoherent cancel ∝ √n → anchor-energy ratio ≈ n; measured 3.0× at k=3, 7.0× at k=7). A general vector is normalised to unit length; e0 (and e4..e7 for the quaternion scope) must be zero._twiddle8now carries all seven imaginary components (was i/j/k only), so a diagonal/general octonion μ is honoured. Existing named-axis behaviour is bit-identical (e4..e7=0).- New public op
srmech.amsc.cascade.hypercomplex_couple(streams, *, axis="diagonal", theta=π/2, sigma=1, form="left", inverse=False)— binds ≥3 streams into one quaternion/octonion + a joint coherence channel and unbinds losslessly. Bind (sigma=+1) appliesT=exp(σ·μ·θ); unbind (sigma=-1) applies the conjugate twiddleexp(−μθ)and recovers the input exactly via the division-algebra identityx̄·(x·y)=‖x‖²·y(F437) — guaranteed reversible only up to 𝕆 (the Hurwitz boundary; sedenion zero-divisors break it) → lossless for ≤7 streams.form/inverse/sigmaare discrete points of the continuous(σ,θ,μ)family = the_one's𝕊(σ,θ)(F420) plus the axis μ. Class M (octonion multiply) ∘ C (σ/conjugation orientation) ∘ N (rational phase θ); no new algebra, no abs() — composite over theqm.octonionleft/right-mult atoms. - These remain COMPOSITES (prototype tier per #863): the 14-class A–N vocabulary is intact. Scientific tier (UPSTREAM §22): numpy is imported lazily on call, so
import srmech.amsc.cascadestays numpy-free.describe()total 236 → 237 (one new tool-schema entry,hypercomplex_couple). ABI unchanged (Python-only cascade composite; no new C symbol). SSoT for the coupling math: Sangwine & Ell (2012) arXiv:1001.4379 (QDFT); Błaszczyk (2019) arXiv:1905.12631 (ODFT); the Hurwitz reversibility cap is Hurwitz (1898).
[0.7.1] - 2026-06-05¶
The Class-L Schur complement / Dirichlet-to-Neumann (DtN) map — the operator|operand FUSION op (#897 §26). Graduation of the rc1–rc3 arc: an op that keeps BOTH a spatial boundary and its spectrum (every other Class-L cascade only projects), integrates the bulk out, and lives on the boundary — S = L_∂∂ − L_∂i·L_ii⁻¹·L_i∂.
srmech.amsc.laplacian.schur_complement(L, boundary_idx, *, exact=False)(aliasdirichlet_to_neumann) — exact-rationalfractions.Fractionsolve (Class-N core; numpy-absent orexact=True) or the float[scientific]-tier realization; the area law is the dimensional reductionn → |∂|. (rc1)- DSL chain-contract wiring —
schur_complementis a first-class cascade-catalog stage;chain().then("schur_complement", boundary_idx=[…])/ TOML chains threadboundary_idx+exactas bound stage kwargs (the 14th catalog op). (rc2) - Reusable native C peer
srmech_dense_solve_f64— the dense linear solveA·X = Bthe Schur/DtN float path composes over (the expensive interior solve IS anA·X = B), promoted to its own exported Class-L primitive. Gauss–Jordan with partial pivoting (Class-K magnitude sign-branch, nofabs/abs()); boundedn,nrhs ≤ 256thread-local workspace (no malloc, reentrant); no libm; JPL Power-of-Ten clean. New public opsrmech.amsc.laplacian.dense_solve(A, B, *, exact=False). (rc3)
describe() total 233 → 236 (schur_complement + dirichlet_to_neumann + dense_solve). ABI stays 3 (additive symbol; the Python ctypes shim hasattr-guards it). Canonical SSoT: Zhang, The Schur Complement and Its Applications (2005) §0; Golub & Van Loan §3. Production cut of the rc3 state already verified-green on TestPyPI (the pedantic-C / 4-cell test / pure-wheel matrix re-verifies the 0.7.1 build); no code change from rc3 — version-string graduation only.
[0.7.1rc3] - 2026-06-05¶
Native C peer for the Schur/DtN float path — a reusable srmech_dense_solve_f64 Class-L primitive (#897 §26). The expensive part of the Schur complement is the interior solve L_ii⁻¹·L_i∂, which IS a dense linear solve A·X = B. rc3 ships that solve as its own exported Class-L C symbol (the "every primitive earns a C surface" path), and schur_complement becomes a genuine composition over it.
c/src/srmech_dense_solve.c→srmech_dense_solve_f64(n, nrhs, A, B, out_X)— Gauss–Jordan with partial pivoting (the pivot magnitude is the Class-K pin-slot read — a sign branch, neverfabs()/abs()); row-major doubles; boundedn, nrhs ≤ 256on a thread-local augmented workspace (thesrmech_hermitian_eigendecomposeprecedent — no malloc, reentrant); a singularAreturnsSRMECH_ERR_BAD_INPUT. No libm — a solve is+ − × ÷only. JPL Power-of-Ten clean (no goto/malloc, ≤60-line factored helpers, ≥2 asserts each); ABI stays 3 (additive symbol,hasattr-guarded in the ctypes shim).- New public Python op
srmech.amsc.laplacian.dense_solve(A, B, *, exact=False)— float path dispatches to the native C peer (numpy fallback when absent / over-bound / singular); exact path is the bit-exactfractions.FractionGauss–Jordan (Class-N, promoted from rc1's_solve_exact). Accepts a matrix or vector RHS. One new tool-schema entry →describe()total 235 → 236. schur_complementnow composes overdense_solvefor its interior solve (both exact and float paths) — the float interior solve runs native, the cheap boundary GEMM + subtract stay numpy. Same results: the rc1 worked instanceS = (1/3)[[1,−1],[−1,1]]is unchanged.tests/test_dense_solve_parity.py: exact + numpy paths always run; native C parity (vs numpy + exact,atol ≤ 1e-9) runs wherelibsrmechis attached.
[0.7.1rc2] - 2026-06-05¶
Schur/DtN wired into the DSL chain contract (#897 §26 follow-up). The rc1 schur_complement op shipped only at srmech.amsc.laplacian.schur_complement; rc2 makes it a first-class cascade-catalog stage so it drives in a chain().then(...) / TOML chain like any other Class-L op. The chain runner resolves stage ops via getattr(srmech.amsc.cascade, name), so the op is re-exported flat onto srmech.amsc.cascade (a DSL-resolution alias of the laplacian-registered op — not a second primitive, so it stays out of cascade.__all__ and adds no tool-schema entry; describe() total holds at 235).
boundary_idx+exactthread through as bound stage kwargs — the data-first pattern (schur_complement(L, *, boundary_idx, exact=False); the pipe fillsL, the kwargs bind), exactly likereorient'sorientation.boundary_idxis required (no default) — a chain that omits it fails loudly withTypeError, never silently. A new descriptorcascade_catalog/schur_complement.toml(class_composition = "L") makes it the 14th catalog op (srmech dsl ops→14 total);schur_complement+dirichlet_to_neumannalso join the documentaryLAPLACIAN_OPSregistry.- Tests (
tests/test_schur_complement_dsl_stage.py):.then("schur_complement", boundary_idx=[0,3], exact=True)and the equivalent TOML[[stage]]both reduce the path-4 graph Laplacian toS = (1/3)[[1,−1],[−1,1]](exactFraction, numpy-free); the float path is numpy-guarded. No ABI change (ABI stays 3); the co-equal native C peer is the rc3 follow-up.
[0.7.1rc1] - 2026-06-05¶
Class-L Schur complement / Dirichlet-to-Neumann (DtN) map — the operator|operand FUSION op (#897; UPSTREAM §26 / F412·F417·F419). New srmech.amsc.laplacian.schur_complement(L, boundary_idx) (alias dirichlet_to_neumann) integrates the interior (bulk) out of a Laplacian and keeps the boundary effective operator
S = L_∂∂ − L_∂i · L_ii⁻¹ · L_i∂
the discrete Dirichlet-to-Neumann map — give boundary values, it returns the boundary normal-derivative of their harmonic interior extension (boundary data ⟹ the whole interior field). Every other Class-L cascade only projects (a spatial graph → its cyclic spectrum, F417's one-way seam, dropping the spatial structure); Schur/DtN keeps both the spatial boundary and its spectrum — the fusion, not the projection. Holographic reading (F412): the bulk is integrated out, the effective theory lives on the boundary; the operator's size is |∂|, not n — the dimensional reduction n → |∂| is the area law.
- Exact-rational core (Class-N). With numpy absent — or
exact=True— the interior solveL_ii⁻¹·L_i∂is exact Gauss–Jordan elimination infractions.Fraction(division is exact rational, never a float reciprocal — F392; noabs()), andSis returned aslist[list[Fraction]]. With numpy present (andexact=False) the float realization rides the[scientific]tier (numpy.linalg.solve) andSis anndarray. Cascade-honesty: the inverse is Class C (conjugate) → Class K (1/‖·‖²); a singular interior block (an interior component disconnected from the boundary) raisesZeroDivisionError, not a silent NaN. - Area-law statement (precise). For a pure graph Laplacian the DtN/Kron reduction inherits the all-ones null vector, so
rank(S) = |∂| − c(c= connected components of the boundary-reduced graph;= |∂| − 1for a connected graph). The area law is the dimensional reductionn → |∂|, not a full-rank claim. Worked check: the two endpoints of a 3-edge unit-conductance path getS = (1/3)·[[1,−1],[−1,1]]exactly (effective conductance ⅓). - Two new tool-schema entries →
describe()total 233 → 235. No ABI change (pure-Python; ABI stays 3). The DSL/compose-engine wiring and a co-equal C peer are the natural follow-up rcs (Python-first, like the loop family). Canonical SSoT: Zhang, The Schur Complement and Its Applications (2005) §0; Golub & Van Loan §3.2.
[0.7.0] - 2026-06-05¶
Production graduation of the v0.7.0 rc1–rc51 arc to PyPI. The clean (non-rc) tag promotes the rc51 state already verified-green on TestPyPI — the only delta from rc51 is this version string + entry, and the full pedantic-C (gcc/clang/MSVC) + 4-cell test matrix + pure-wheel build re-verify the 0.7.0 build before the production tag. ABI 3; describe() total 233.
The v0.7.0 identity. numpy is now optional: pip install srmech is numpy-free — the 14-class A–N cascade core (srmech.amsc.*) and the native C surface run with zero numpy; pip install 'srmech[scientific]' pulls numpy back in for the array-numerical tier (srmech.qm.* / signal_processing.* / rbs_lm.*). Every continuous-math op (trig, exp, sqrt, FFT, SVD, eig) is a cascade of the 14, and the shipped libsrmech holds no libm transcendental — the executable runs the Class-N cascade, not the C math library.
The arc, voxel by voxel:
- MS #21 — the Moufang loop-bind / octonion gauge arithmetic (rc1–rc7).
srmech.amsc.hdcgains the octonion (Cayley–Dickson) productloop_bind+loop_conj/loop_inv/loop_associator/loop_left_op/loop_right_op;cross7(the 7-D cross product,M∘C) + the G₂ associative 3-form; the block-octonion HD tiling (loop_bind_hd, D=2048); compose-engine integration (the loop family resolves asclass="M"); co-equal C peers inc/src/srmech_loopbind.c. - Perf + attestation discipline (rc8–rc20). The Class-L circular autocorrelation primitive (Wiener–Khinchin); the N-way SIMD SHA-256 batch (AVX2 8-way / SSE2 4-way runtime cpuid dispatch, JPL-clean) routed through the new
c/src/srmech_simd.hHAL — all platform/cpuid/target-attr bits live in the HAL, the core stays machine-agnostic; HAL constant-attestation (MPR derive-and-assert); native C peers for the HD loop family. - #797 ops + the §22 numpy-free cascade core (rc26–rc46).
asymptotic_calculus/trigonometryalias modules + a directed/signed-Laplacian eigen-op; the Klein-4 holographic-erasure code + the explicit order-3 triality-recursion corrector; the numpy-free HV carrier. The §22 arc makes every continuous-math op a cascade: QDFT/ODFT, laplacian-build + pure-Python Jacobi eigenvalues, dft/idft/kron, radix-2 Cooley–Tukey FFT, QR/SVD, lstsq/einsum/non-Hermitian eig; themath.sqrt+ trig/π residue sweeps onto the Class-N rationals; the C-transpile coherence arc driving libsrmech's libm-transcendental count23 → 0(native Csrmech_sin/cos/atan/atan2/rational_sqrt/exp/log). - numpy → optional (rc47–rc48). numpy demoted from a hard dependency to the
scientificextra, with a friendlypip install 'srmech[scientific]'gate at the scientific-tier import boundary; the #882 lazy-numpy fix (the Klein-4 HV-carrier path runs genuinely numpy-free on a plain install). - "The One" (rc49–rc50, #887).
S(σ,θ) = ⨁ₙ(ℝ·1 ⊕ σ·e^{Îₙθ}·Im 𝔸ₙ),dim = 2+4+8 = 14— the single generator of the1+3+7+3 = 14substrate, shipped as a Rosetta pair: the numpy-free exact-rationalsrmech.amsc.cascade.the_one+ the bit-exactsrmech.qm.hurwitzmatrix peer (to_matrix() == hurwitz_matrix();FANO_PLANES == hurwitz_planes()derived fromoctonion_mult_table). The 0/⅓ finding:e^{Îₙθ}is the algebra's own rotation → turns 0/⅓ Fano planes for ℂ/ℍ/𝕆; 𝕆 spins three planes at once (eigenvalues{1, e^{±iθ}×3}; the 7 = 1 fixed axis + 3×2 rotated). The ℂ/ℍ/𝕆 Hurwitz ladder =so(8)+ Spin(8) triality (Fix(τ) = g₂ = 14). - PyPI README + description refresh (rc51). Cut the QM/QFT/SM framing — every continuous-math op is a cascade of the 14, so no math domain is privileged or called out (the physics worked-examples still ship and stay discoverable via
describe()/ the tool-schema).
No code change from rc51; version-string graduation + this entry only.
[0.7.0rc51] - 2026-06-05¶
PyPI-facing README + project description refresh (pre-graduation). Cuts the "canonical QM/QFT/SM operations" framing — every continuous-math op is a cascade of the 14, so no particular math domain is privileged or called out (it is all the same cascade). No code change; the physics worked-examples (single_particle/spin/relativistic/propagators/gauge/sm) still ship and remain discoverable via describe() / the tool-schema.
[project].description(bothpyproject.toml+pyproject-pure.toml, kept in lockstep) — rewritten to the v0.7.0 identity: numpy-optional 14-class A-N vocabulary in native C + Python; every continuous-math op (trig/exp/sqrt/FFT/SVD/eig) is a cascade of the 14, no libm in the native build; the One,S(σ,θ), generates the whole1+3+7+3 = 14substrate (the ℂ/ℍ/𝕆 Hurwitz ladder =so(8)+ Spin(8) triality, bit-exact cascade↔matrix); AMSC (MPR v1). ASCII-only, 438 chars (under the 480 soft cap).README.md— status banner refreshedv0.6.0 → v0.7.0; the QM/QFT/SM feature bullet replaced by the continuous-math cascade + the One; thesrmech.qm.*section retitled the substrate engine (the ℂ/ℍ/𝕆 Hurwitz ladder,so(8)triality, the One) —octonion/so8/trialitykept,hurwitz(the One's matrix peer, #887) added, the physics-ops enumeration dropped (with a domain-neutral note that they still ship, discoverable, un-privileged); stale0.6.0example outputs bumped to0.7.0.
No ABI / API change; describe() total unchanged at 233.
[0.7.0rc50] - 2026-06-05¶
"The One" goes octonion-native — S(σ,θ)'s 𝕆 block is a 3-plane rotation, with a bit-exact qm-matrix Rosetta peer (#887). rc49 used the simplest single-plane epicycle for every block; rc50 makes e^{Î_nθ} the algebra's own rotation — conjugation by the unit cos(θ/2)+Î_n sin(θ/2), which turns every Fano-triple plane through Î_n by θ. The plane count is 0 / 1 / 3 for ℂ / ℍ / 𝕆: the single θ-turn spins three planes at once in 𝕆 (eigenvalues {1, e^{±iθ}×3} on the imaginary part — the 1 fixed axis + 3×2 rotated split of the 7). ℂ (σ-only) and ℍ (1-plane) are unchanged.
srmech.amsc.cascade.one— the rotation now usesFANO_PLANES(the oriented Fano lines through each axis: ℍ(1,2,+1); 𝕆(1,6,−1),(2,5,+1),(3,4,+1)throughÎ₃=e₇), matching the fixed Cayley–Dickson-from-ℍ convention (Baez 2002 §2).One.to_matrix()is the full block-diagonal multi-plane operator; newOne.plane_counts→(0,1,3)andBlock.rotated_planes. The 𝕆 seede₁now lands in its Fano plane(1,6,−1)→cosθ·e₁ − sinθ·e₆(the only rc49 behaviour change).- NEW
srmech.qm.hurwitz— the scientific-tier matrix peer:hurwitz_matrix(σ, θ)builds the same14×14G(σ,θ), andhurwitz_planes()derives the planes straight fromoctonion_mult_table(not a hardcoded list). The cascade and the qm matrix agree bit-for-bit (np.array_equal), and the hardcodedFANO_PLANESequals the table-derivedhurwitz_planes()— a genuine two-language cross-derivation (continuous-Hopf matrix vs discrete-cyclic cascade), not a restatement. - No new primitive class (Class A planes ∘ N rational cos/sin ∘ K·C sign), no
abs(). Two new tool-schema entries →describe()total 231 → 233. No ABI change.
[0.7.0rc49] - 2026-06-05¶
"The One" — S(σ,θ), the single generator of the 1+3+7+3 = 14 substrate (#887). A new cascade-native surface srmech.amsc.cascade.the_one builds the unifying Hurwitz-ladder generator
S(σ,θ) = ⨁_{n=1}^{3} ( ℝ·1 ⊕ σ·e^{Î_nθ}·Im 𝔸_n ),dim = Σ 2ⁿ = 2+4+8 = 14
with 𝔸₁=ℂ, 𝔸₂=ℍ, 𝔸₃=𝕆 (the normed division algebras above ℝ). The decomposition is the A–N partition: the imaginary parts Im 𝔸_n (dims 1, 3, 7) carry the anchor A / projection-triad I,C,J / detection-heptad D,E,F,G,K,L,M; the three ℝ·1 real units are the +3 grammar B, H, N.
- Numpy-free, exact-rational.
e^{Î_nθ} = cos θ + Î_n sin θis built from the Class-N rational Taylor partials (rational.{cos,sin}_series_truncate) — every entry is a reduced(num, den)integer pair; no float until the opt-inOne.to_numpy()/One.to_matrix()realisations (thesrmech[scientific]tier, §22). No new primitive class —⨁overnis Class I,σis Class K sign ∘ Class C apply (neverabs()). - Structural prediction: n=1 degenerates to σ. Fixing the rotation axis
Î_n = e_d(the last imaginary unit) and rotating the(e₁,e₂)plane, atn=1the 1-DIm ℂseed coincides with the axis →θis inert and the only freedom is the chiralityσ(the epicycle is the Class-K sign at the foundational algebra; richness grows1→3→7). Verified bit-exactly. - Returns a structured
Oneof threeBlocks tiling1+3+7+3;.dim,.partition,.grammar_slots,.n1_is_sigma_only,.to_flat_rational(). The qm-matrix Rosetta peer (srmech.qm.hurwitz) + the bit-exact cascade↔matrix parity test follow in rc50. No ABI change;describe()unchanged. Python-only (the C-transpile triality ratchet stays at 0).
[0.7.0rc48] - 2026-06-05¶
Fix #882: srmech.amsc.hdc (Class M / Klein-4) — and three sibling amsc core modules — no longer crash raw on a plain (numpy-free) install. rc47's numpy-optional capstone left four srmech.amsc.* modules with a top-level import numpy as np, so import srmech.amsc.hdc raised a raw ModuleNotFoundError: No module named 'numpy' instead of importing cleanly (the Klein-4 HV-carrier path is designed numpy-free) or gating like srmech.qm. rc47's AST ratchet used a hardcoded module list that missed them.
srmech._scientific.lazy_numpy— a lazy numpy proxy: the holding module imports numpy-free; the first numpy attribute access imports numpy or raises the actionablepip install 'srmech[scientific]'hint.srmech.amsc.{hdc, coupling, harmonics, cascade.matrix_cascades}now use it.- Result, on a plain install: the modules import; the Klein-4 HV-carrier path (
klein4_randomdefault-seed /klein4_bind/klein4_bundle/klein4_similarity/ chirality / triality / holographic) runs genuinely numpy-free; the bipolarpolar_*HDC + the loop family + the QR/SVD/lstsq/einsum/eig matrix cascades raise the clean[scientific]hint when called. The issue's preferred option (a). - Ratchet broadened —
test_numpy_optional_rc47.pynow walks the wholesrmech/amsc/**subtree for module-level numpy imports (closing rc47's hardcoded-list hole), plus a numpy-blocked behavioral test (import + Klein-4 numpy-free + the[scientific]hint). numpy-present behavior unchanged; no ABI change;describe()stays 230.
[0.7.0rc47] - 2026-06-05¶
numpy is now an OPTIONAL dependency — the §22 capstone. The §22 + C-transpile arcs (rc29–rc46) made the Class-N cascade core numpy-free: srmech.amsc.* (the A-N primitives, the rational/cyclic/laplacian cascades) and the native C surface run with zero numpy. rc47 demotes numpy from a hard dependency to the scientific extra. pip install srmech is now numpy-free; pip install 'srmech[scientific]' pulls it back in for the array-numerical scientific tier (srmech.qm.* / srmech.signal_processing.* / srmech.rbs_lm.*).
pyproject.toml+pyproject-pure.toml— numpy moved out ofdependenciesintooptional-dependencies.scientific. Thedev+testsextras keep numpy (the full suite exercises the scientific tier). No ABI change;describe()stays 230.- Friendly gate — new numpy-free
srmech._scientific.require_numpy; the scientific-tier subpackages (qm/signal_processing/rbs_lm) call it at import so a no-numpy install fails withpip install 'srmech[scientific]', not an opaqueNo module named 'numpy'.ImportError(numpy's own error subclasses it), so existing handlers keep working. - CI guard — the pure-wheel "Verify wheel installs + imports" job now asserts numpy is absent from the base install, that the cascade core works numpy-free, and that the scientific tier raises the actionable hint. New
test_numpy_optional_rc47.pypins the pyproject contract + the gate + an AST ratchet that the cascade core never imports numpy at module top.
This is the last planned rc of the v0.7.0 line. Graduation to production PyPI is held for a dedicated testing pass.
[0.7.0rc46] - 2026-06-05¶
The C-transpile triality closeout — the executable runs the Class-N cascade, not libm (C ratchet → 0). The shipped libsrmech now holds no libm transcendental: the notebook, the C+Python source, and the native executable all agree. ABI stays 3 (additive srmech_exp/srmech_log); describe() stays 230.
- New
c/src/srmech_explog.c→srmech_exp/srmech_log, the double→double Class-N exp/log cascades (the last two libm calls in the library): srmech_exp(x)— range-reducex = n·ln2 + r(|r| ≤ ln2/2, two-word Cody–Waiteln2),exp(r)via a Q61 integer Taylor1 + r + r²/2! + …, then· 2^nwith the power-of-two built straight into the IEEE exponent field (noldexp). Overflow →+Inf, underflow →0.srmech_log(x)— readx = m·2^eexactly from the bit pattern (nofrexp), foldminto[1/√2, √2),log(m) = 2·atanh((m−1)/(m+1))via a Q61 integer atanh series, then thee·ln2recombine (two-wordln2). Non-positivex→SRMECH_ERR_BAD_INPUT(NaN / −Inf).- No libm, no
abs()(Class-K sign-branch). Machine-ε vs libm (exp rel err ≤ 2.3e-16; log abs err ≤ 2.3e-16 over 500k values;exp(log(x))round-trips ≤ 2.3e-16). srmech_laplacian.crepointed — the elementwiseexp/log→srmech_exp/srmech_log;#include <math.h>dropped (the file now holds no libm). Class L composes Class N in the executable.srmech_kepler.c— the lastfabs(Newton convergence test) → an explicit Class-K sign-branch;#include <math.h>dropped (no libm in the file).- C ratchet
test_c_cascade_coherence.py→ 0 (23 → 16 → 13 → 3 → 0across rc43–rc46). The guard now also covers the C99 complex libm (csin/ccos/cexp/csqrt/…) and any<complex.h>include, so a future complex op can't silently reintroduce libm. JPL-clean; pedantic-Werror//WXclean.
Next (the capstone, human-gated): the numpy→srmech[scientific] optional-dependency flip, then the clean v0.7.0 graduation to production PyPI.
[0.7.0rc45] - 2026-06-05¶
Native sqrt cascade — the Jacobi eigensolver runs the Class-N integer-sqrt, not libm (C-transpile triality, arc step 3). ABI stays 3 (additive srmech_rational_sqrt); describe() stays 230.
- New
c/src/srmech_sqrt.c→srmech_rational_sqrt—sqrt(x)(x≥0) via an integer floor-isqrt on a scaled radicand:x = M·2^eread from the bit pattern,root = isqrt(M<<54)via a portable two-limb 128-bit integer square root (restoring binary, 64 bounded iterations, no division, no__int128), projected by(double)root·2^(e/2−27)with the power-of-two built directly from the IEEE exponent field (noldexp). No libm, no float sqrt. Machine-ε vs libm (rel err ≤ 2.2e-16 over 500k values;1/√dbit-exact). srmech_laplacian.crepointed — the 8 cyclic-Jacobisqrt(off-diagonal norm + rotation angles) → alap_sqrtwrapper over the cascade; the elementwisecos/sin→srmech_cos/srmech_sin. Class L now visibly composes Class N in the executable. Jacobi spectrum vsnumpy.linalg.eigvalshunchanged.- C ratchet baseline 13 → 3 (laplacian 12 → 2: only
exp+logremain). JPL-clean; pedantic-Werror//WXclean.
Roadmap (rc46, the closeout): a C exp double-wrapper over srmech_exp_series_truncate + a C log (log1p-series + integer exponent) → repoint the laplacian signed/magnetic phase; fabs→Class-K sign-branch (srmech_kepler.c); the complex-libm guard (csin/ccos/cexp/csqrt) → C ratchet 0, executable fully on the cascade. Then the numpy→srmech[scientific] capstone.
[0.7.0rc44] - 2026-06-05¶
Native Kuramoto step runs the trig cascade, not libm (C-transpile triality, arc step 2). ABI stays 3; describe() stays 230. srmech_cascade_kuramoto_step_f64 / _general_f64 now compute their coupling sin via the rc43 Class-N cascade.
srmech_kuramoto.crepointed — the 3 libmsinsites (mean-field couplingΣ sin(θⱼ−θᵢ), the generalised SakaguchiΣ Aᵢⱼ·sin(θⱼ−θᵢ−α), and the per-oscillator pinningpᵢ·sin(ψᵢ−θᵢ)) →srmech_sin;#include <math.h>dropped (no other libm in the file).- #784 / F234 non-regression confirmed — the Kuramoto-coupled-adder differential test (
test_kuramoto_step.py, the "tile nibbler") stays green with the native cascade-trig (18 passed). The defaults still reproduce the plain step. - C ratchet
test_c_cascade_coherence.pybaseline 16 → 13 (kuramoto 3 → 0). JPL-clean; pedantic-Werror//WXclean.
Roadmap: rc45 srmech_rational_sqrt (integer-Newton) → srmech_laplacian.c (sqrt×8 Jacobi + the cos/sin/exp phase); rc46 fabs→Class-K sign-branch + the complex-libm guard (csin/ccos/cexp/csqrt) → C ratchet 0. Then the numpy→srmech[scientific] capstone.
[0.7.0rc43] - 2026-06-05¶
Native C trig cascade — the executable runs the Class-N cascade for trig, not libm (C-transpile triality, rc42→rc46 arc step 1). ABI stays 3 (additive C symbols); describe() stays 230 (no Python tools). The first behavioural C port: on a native install kepler.{pin_slot,kepler_solve,equation_of_centre} now compute sin/cos/atan2 via the cascade, closing the kepler row of the executable-coherence gap.
- New
c/src/srmech_trig.c→srmech_sin/srmech_cos/srmech_atan/srmech_atan2(double→double, status-returning). The cyclic range-reduction (mod π/2) is pure INTEGER (user direction "prefer ints over float always for cyclic algebra"): the IEEE-754 input is read as an exactM·2^Efrom its bit pattern (nofrexp), the octantkcomes from an integer wide-multiply (portable 64×64→128, no__int128/_umul128) by a high-precision cascade2/π, the remainder is an exact integer fraction; the Class-N Taylor runs in Q61 fixed-point; float appears only at the final(double)sum/2^61projection. π from the Archimedes pi-cascade (derive-and-assert constants). No libm, noabs()(Class-K sign-branch). Validated vs libm to machine ε — sin 5.3e-19, cos 8.1e-20, atan 2.2e-16, atan2 0.0 bit-exact across 200k+ angles. srmech_kepler.crepointed — 7 trig sites (cos×2,sin×4,atan2×1) → the cascade; nativepin_slotis bit-exact (0.0) vs libm and the Kepler residual holds at 0.0. (Thefabsstays for rc46.)- C ratchet
test_c_cascade_coherence.pybaseline 23 → 16 (kepler 8 → 1). New ctypes parity testtest_native_trig_rc43.py. JPL Power-of-Ten clean (≤60-line fns, ≥2 asserts); pedantic-Werror//WXclean. Kuramoto native step (#784) confirmed green pre-touch (rc44 target).
Roadmap: rc44 repoints srmech_kuramoto.c (sin×3, watching the F234 differential test); rc45 adds srmech_rational_sqrt → srmech_laplacian.c; rc46 fabs→sign-branch + the complex-libm guard (csin/ccos/cexp/csqrt) → C ratchet 0. Then the numpy→srmech[scientific] capstone.
[0.7.0rc42] - 2026-06-05¶
C-transpile triality coherence — start the native/executable-tier port, ratchet-first. Pure tooling+docs; no new tools (describe() stays 230); ABI stays 3; no C behaviour change yet. Opens the rc42→rc46 arc that makes the executable layer run the Class-N cascade, not libm.
- The gap rc40/rc41 didn't close. Those sweeps were Python-only (AST ratchets walk
*.py). On a native install (HAS_NATIVE=True— the default)kepler.{pin_slot,kepler_solve,equation_of_centre}/cascade.kuramoto_step/ signed-Laplacian dispatch to C peers that still call libmsin/cos/atan2/sqrt/pow/fabs. So the three coherence layers — notebook / C+Python source / executable — agree numerically (rational ≡ libm to machine ε, which masked it) but the C isn't a faithful transpile of the cascade. Onlyexpalready coheres (srmech_exp_series_truncate). - New ratchet
tests/test_c_cascade_coherence.py— a DOWN-only baseline ratchet (same shape astest_jpl_audit.py) over the shipped libraryc/src+c/include(c/test/*excluded). Records the current 23 libm/π sites (srmech_kepler.c8 +srmech_kuramoto.c3 +srmech_laplacian.c12); each file's count + the total only go DOWN, and no new C file may introduce libm transcendentals. Ships green (baseline = reality) — the gap is now measured + visible. - Notebook
notes/continuous_math_as_14_class_cascade.md— new "C-transpile triality coherence" section: the three-layer coherence model + the per-op table + the rc42→rc46 roadmap, grounded in the RBS-LMnative-algebra compute surfacefindings (F305/F306, PR #687, read-only).
Roadmap (rc43–rc46): srmech_{sin,cos,atan,atan2}_series_truncate + C pi_cascade → repoint srmech_kepler.c (rc43); srmech_kuramoto.c (rc44); srmech_rational_sqrt → srmech_laplacian.c (rc45); fabs → Class-K sign-branch, C ratchet → 0 (rc46). Then the numpy→srmech[scientific] optional-dependency-flip capstone. New C symbols are additive (ABI stays 3).
[0.7.0rc41] - 2026-06-05¶
math.{sin,cos,atan2} + math.pi trig/π residue sweep — route continuous trig + π through the Class-N cascade, not libm. Pure refactor; no new tools (describe() stays 230); ABI stays 3. The companion to rc40's math.sqrt sweep, closing the §22 libm-scalar-math audit: now that rational.{sin,cos,tan,atan,atan2} (rc33) + the pi_cascade exist, every remaining libm trig / π reference in shipped srmech is routable to its exact Class-N peer.
- 14 sites routed across 4 modules — verified machine-ε vs libm before routing (sin 7.8e-16, cos 6.7e-16, atan2 4.4e-16 across all quadrants + multiple periods; the cascade-π float is bit-exact 0.0 vs
math.pi): amsc/kepler.py×7 —cos/sin/atan2in the pin-slot transform + the Kepler-equation Newton solver + the equation-of-centre series →rational.{cos,sin,atan2}. The iterative solver is the sensitive case (trig accuracy gates Newton convergence):pin_slotis bit-exact 0.0 vs libm and the Kepler residual|E − e·sinE − M|holds at 4.4e-16. (The Class-K Newton-step magnitude was already an explicit sign-branch, neverabs().)amsc/cascade/compose.py×3 — thesinin the Kuramoto-coupling DSL worked examples →rational.sin(math.fsumstays — exact numerical sum, no transcendental peer).amsc/cascade/hypercomplex_dft.py×2 +math.pi×1 — the quaternion/octonion twiddleexp(μθ)=cosθ + μ·sinθ+ the2πfactor →rational.{cos,sin}+ a cascade-π float (pi_cascade_digits(30)projected once at import).import mathdropped.signal_processing/form_function_rotation.py×1 — themath.piin the fundamental-mode eigenvalueexp(−2πi·composed/D)→ cascade-π (its trig was alreadyrational.cos/sin).import mathdropped.- Ratchet
test_no_math_trig_pi_anywhere_in_srmech(AST) — nomath.{sin,cos,tan,asin,acos,atan,atan2,exp,pi,tau}reference (call or bare constant) anywhere in shipped srmech; only goes DOWN to zero.math.{gcd,isqrt,fsum}(exact integer / numerical helpers —isqrteven powersrational.py's own sqrt cascade) are deliberately NOT flagged. - Audit note
notes/sqrt_sweep_rc40.md— the rc41 residue section marked routed; the libm-scalar-math audit (26 references across 8 files) is now fully swept (12 sqrt @ rc40 + 14 trig/π @ rc41).
With rc41 the libm-scalar-math discipline is complete — no math.{sqrt,hypot,sin,cos,tan,atan,atan2,exp,pi,tau} anywhere in shipped srmech; continuous scalar math routes through the A–N cascade (rational.* + the pi_cascade), with numpy/cmath retained only for genuinely-vectorised or complex-root ops that have no scalar-cascade peer. Roadmap: the numpy→srmech[scientific] optional-dependency-flip capstone. No new C symbols.
[0.7.0rc40] - 2026-06-05¶
math.sqrt scalar-site retrofit sweep — route the scalar root through the Class-N cascade, not libm. Pure refactor; no new tools (describe() stays 230); ABI stays 3. The §22 discipline closeout (sibling of the rc32 abs()-sweep and rc33 numpy-math-sweep): now that rational.sqrt/hypot exist (rc35), every math.sqrt in shipped srmech is routable to its exact Class-N peer.
- 12
math.sqrtcall sites routed →srmech.amsc.rational.sqrt, across 5 modules: amsc/laplacian.py×5 — the cyclic-Jacobi eigensolver's off-diagonal norm + rotation angles. Class L now visibly composes Class N (its leaf root is the rational sqrt). Jacobi vsnumpy.linalg.eigvalsh= 8.9e-16.qm/bell.py×2 (Tsirelson2√2,1/√2),qm/octonion.py×1 (octonion_norm),qm/sm.py×3 (Higgs vev / Z-mass / Yukawa),amsc/cascade/hypercomplex_dft.py×1 (1/√3) — all bit-exact 0.0 vs libm (rational.sqrtis machine-ε at 64 precision bits). All 12 args are provably non-negative (guards / sums-of-squares), sorational.sqrt's raise-on-negative is a safe drop-in.- Ratchet
test_no_math_sqrt_hypot_anywhere_in_srmech(AST) —math.sqrt/math.hypotcalls only go DOWN to zero.cmath.sqrt(complex root, the rc39 eigvals shift) andnp.sqrt(array)(bulk-array, no scalar peer) are NOT flagged. - Audit note
notes/sqrt_sweep_rc40.md— the full AST audit found 26 routablemath.*references; the 12 sqrt are routed here, the 14 trig/pi residue (math.{sin,cos,atan2}×12 in kepler/compose/hypercomplex_dft +math.pi×2) are STAGED to rc41 (a different primitive family with its own anchor concerns + kepler's iterative solver).rational.py/pi_cascade.py/trigonometry.pyhave ZERO real libm calls — the float-trig cascades are genuinely libm-free.
Roadmap: rc41 = the math.{sin,cos,atan2}/math.pi trig-residue sweep onto rational.{sin,cos,atan2} + pi_cascade; then the numpy→srmech[scientific] dependency-flip capstone. No new C symbols.
[0.7.0rc39] - 2026-06-05¶
lstsq + einsum + non-Hermitian eig — the remaining linear-algebra layer as A–N cascades. ABI stays 3; describe() 227 → 230 (+3 tools). numpy is the array container only — no np.linalg.{lstsq,eig,eigvals} in the call graph (AST guard).
- New in
srmech.amsc.cascade.matrix_cascades: lstsq— least-squaresmin‖A x − b‖= {QR} factorization (rc38'sqr) ∘ Class M (theQᴴ bproduct) ∘ Class I (back-substitution, the ordered triangular solve). Overdetermined/squarem ≥ n;ba vector or a stack of RHS. Matchesnumpy.linalg.lstsq(a,b)[0]to ~1e-15.einsum— the tensor contraction = Class B/D (the subscript string is a typed index-pattern spec) ∘ Class I (iterate over every free + summed index tuple) ∘ Class M (the sum-of-products bundle). The general index-iteration definition — handles any subscript string (matmulij,jk->ik, traceii->, transposeij->ji, doti,i->, outeri,j->ij, batchedijk,kl->ijl, implicit output), just unoptimised. Bit-exact / machine-ε vsnumpy.einsum.eigvals— non-Hermitian eigenvalues via the shifted-QR iteration: Class K (iterate-to-convergence asymptotic-DoF) ∘ Class L (the spectral content) ∘ {QR} (the per-step Householder factorization) ∘ Class C (the Wilkinson spectral shifts). Runs in complex arithmetic, so complex eigenvalues of real matrices fall out directly — the 2-D rotation[[0,−1],[1,0]]yields±i. The eigenvalue multiset matchesnumpy.linalg.eigvalsto ~1e-12 (Hermitian inputs are the already-shipped exact special case = pure Class L, the cyclic Jacobi).- MCP-callable — params are
np.ndarray/str/int/tuple[np.ndarray,…], all with existing coercers. - Tests
tests/test_lstsq_einsum_eig_rc39.py(+10): lstsq overdetermined/square/multi-RHS vs numpy, einsum across 9 subscript shapes + complex, eigvals multiset-match across 20 random real+complex matrices, complex-conjugate-pair-of-real, empty/scalar/non-square edges, AST guard.
With rc39 the continuous_math_as_14_class_cascade table is complete — every op once parked in the §22 "scientific tier" (exp/cexp/sqrt/hypot/DFT/FFT/kron/QR/SVD/lstsq/einsum/eig) now has a shipped A–N cascade. Roadmap: rc40 = the codebase-wide math.sqrt/np.hypot scalar-site retrofit sweep onto rational.{sqrt,hypot} (focused discipline pass); then the numpy→srmech[scientific] dependency-flip capstone. No new C symbols.
[0.7.0rc38] - 2026-06-05¶
QR + SVD as A–N cascades — the matrix factorizations, built on srmech's own roots + eigendecomposition. ABI stays 3; describe() 225 → 227 (+2 matrix tools). numpy is the array container only — there is no np.linalg.qr/np.linalg.svd anywhere in the call graph (an AST guard enforces it).
- New
srmech.amsc.cascade.matrix_cascades.{qr, svd}: qr—A = Q·Rvia Householder reflections. Q is a product (Class M) of elementary reflectorsH = I − β·v·vᴴ; each reflector is Class K (the sign-flip across a hyperplane) ∘ Class M (the outer-productv·vᴴbind) ∘ Class N (the2/(vᴴv)scale, the column norm arational.sqrt). The phase choiceα = −phase·‖x‖is the Class K pin-slot that avoids cancellation.mode='reduced'(default, matchingnumpy.linalg.qr) /'complete'. Real + complex.svd—A = U·diag(s)·Vᴴreached from the Hermitian eigendecomposition of the Gram matrix: Class L (hermitian_eigendecomposeofAᴴAorAAᴴ— srmech's cyclic-Jacobi cascade) ∘ Class N∘K (s = √eigvalsviarational.sqrt) ∘ Class M (U = A·V·Σ⁻¹).- Verified by INVARIANTS, not by element-wise numpy match (QR/SVD are unique only up to signs): reconstruction
Q·R = A/U·diag(s)·Vᴴ = Ato ~1e-15, orthonormalityQᴴ Q = I/Uᴴ U = Ito ~1e-14, R upper-triangular — and the singular VALUES (which are unique) matchnumpy.linalg.svdto round-off. The Gram route squares the condition number, so very small singular values carry √ε-scale error (documented caveat). - MCP-callable — params are
np.ndarray/str/bool, all with existing coercers; the every-tool smoke covers both. - Tests
tests/test_matrix_cascades_rc38.py(+8): QR/SVD invariants across real+complex and 7 shapes, complete-mode, singular-value match vs numpy, empty, AST guard (noabs(), nonp.linalg.{qr,svd,eig,…}).
Roadmap: rc39 = lstsq ({QR}∘M∘I back-substitution) + einsum (B/D∘I∘M index-iteration) + non-Hermitian eig (K∘L∘{QR}∘C, the shifted-QR iteration); rc40 = the codebase-wide math.sqrt/np.hypot scalar-site retrofit onto rational.{sqrt,hypot} (a focused discipline pass, like the rc32 abs-sweep / rc33 numpy-math-sweep). The numpy→srmech[scientific] dependency-flip is the capstone. No new C symbols.
[0.7.0rc37] - 2026-06-04¶
The FFT butterfly = the DFT cascade + Class J + Class K. Pure-Python; ABI stays 3; describe() 223 → 225 (+2 FFT tools).
- New
srmech.amsc.cascade.spectral_cascades.{fft, ifft}— the radix-2 Cooley–Tukey butterfly. Bit-for-bit the same mathematics as rc36'sdft(and value-faithful tonumpy.fft.fft/ifftto ~3e-14, machine ε), butO(N log N)whenNis a power of two: the decimation-in-time even/odd splitx[0::2]/x[1::2]is Class J (the radixN = 2·(N/2)factorization), the recursion is Class K (the butterfly depth), the twiddlee^(∓2πi·k/N)is the samecexp= Class N ∘ Class C, and the butterflyE ± t·Ois Class M (bundle add) ∘ Class K (pin-slot sign-flip). Nomath.pi/np.piin the call graph (π from the cascade). - Full-coverage at any length — for non-power-of-2
N(3, 5, 7, 13, …)fftfalls back to rc36's directO(N²)dft, so it is a true drop-in fornumpy.fft.fft/ifftat anyN, not just powers of two. (The general mixed-radix butterfly — full Class J overN's prime factorization — is the follow-on refinement.) - MCP-callable —
fft/ifftreuse rc36'slist[complex]coercer; no new coercion handler needed. - Tests
tests/test_fft_radix2_rc37.py(+7): fft/ifft vs numpy across power-of-2 AND non-power-of-2 lengths, fft≡dft agreement, ifft∘fft round-trip, empty,_is_power_of_twoexhaustive 1..129, AST no-libm-π guard.
Roadmap: rc38 = QR (Givens/Householder) + SVD (from hermitian_eigendecompose) + the math.sqrt/np.hypot scalar-site sweep onto rational.{sqrt,hypot}; rc39 = non-Hermitian eig + lstsq + einsum; the numpy→srmech[scientific] dependency-flip is the capstone. No new C symbols.
[0.7.0rc36] - 2026-06-04¶
The DFT as the Antikythera epicycle-sum + Kronecker, as A–N cascades — plus a Bio-TOTP test-flake root-cause + fix. Pure-Python; ABI stays 3; describe() 220 → 223 (+3 spectral cascade tools).
- New numpy-free spectral cascades
srmech.amsc.cascade.spectral_cascades.{dft, idft, kron}(built on rc34'scexp): dft/idft— the discrete Fourier transform IS the Antikythera epicycle-sum ([[user_stance_epicycle_via_gear_plus_pin]]):X_k = Σ_n x_n · e^(∓2πi·(k·n mod N)/N)= Class I (the cyclic indexk·n mod N) ∘ Class N (the twiddle cos/sin) ∘ Class C (the imaginary-unit 90° rotation) ∘ Class M (the bundle/superposition sum). DirectO(N²); matchesnumpy.fft.fft/ifftto ~3e-15 (machine ε). Nomath.pi/np.piin the call graph (the twiddle angle draws π from the cascade). The radix-2O(N log N)butterfly (adds Class J + Class K) is the follow-on.kron— Kronecker productA⊗B= Class I (mixed-radix index) ∘ Class M (element products). Bit-exact vsnumpy.kron.- MCP-callable: the new
list[complex]/list[list[complex]]param types get real inbound coercers insrmech.mcp._coercion(each complex scalar rides as[re, im]), so all three tools are invocable over the MCP / Anthropic surface (the every-tool invocation smoke covers them). - Bug fix (test-only): Bio-TOTP flaky test root-caused.
test_bus.py::test_bio_totp_decrypt_rejects_channel_id_mismatchwas intermittently failing withDID NOT RAISE(~⅛). Root cause: the test runs in permissive mode (strict=False) and used the real wall clock, soencrypt/decryptcould straddle a TOTP window boundary — the wrong-window decrypt yields garbage that fails the UTF-8/JSON parse, so the binding fields read as absent and permissive mode accepts (routing around the present-but-mismatched rejection the test exercises). The cipher and the securestrict=Truebus path are unaffected (strict mode rejects garbage). Fixed by pinningtime_ns(the same hook every deterministic sibling test already uses) on this test and the sibling replay test; verified deterministic over 80 executions. - Tests
tests/test_spectral_cascades_rc36.py(+6): dft/idft vs numpy.fft, idft∘dft round-trip, kron vs numpy.kron, AST no-libm-π guard.
Roadmap: rc37 routes the ~32 math.sqrt/np.hypot sites + the radix-2 FFT + QR/SVD; the numpy→srmech[scientific] dependency-flip is the capstone. No new C symbols.
[0.7.0rc35] - 2026-06-04¶
sqrt/hypot cascade primitives + module rename asymptotic_calculus → calculus. Pure-Python; ABI stays 3; describe() 218 → 220 (+2 root tools).
- New substrate-native roots —
srmech.amsc.rational.{sqrt, hypot}(re-exported fromsrmech.calculus).sqrt(x)(x ≥ 0) is Newton-Raphson realised as an integer floor-isqrt on a scaled-bignum radicand — Class-N rational arithmetic ∘ Class-K sqrt-convergence; nomath.sqrt/np.sqrtin the call graph (AST-guarded); negativexraises a domain error.hypot(a, b) = √(a²+b²)= Class-M sum-of-squares bind ∘ the Class-N sqrt (the complex modulus|z| = hypot(z.real, z.imag)). Both bit-exact vs libm in testing. - Module rename
asymptotic_calculus→calculus(no-break). The continuous-calculus surface is nowsrmech.calculus;srmech.asymptotic_calculusremains a back-compat re-export alias (same function objects). The "asymptotic" qualifier was an early framing that singled out this one module while the whole framework is equally substrate-native asymptotic-rational (trig, exp, the eigen ops, the FFT-as-epicycle-sum) — the insight is framework-wide now (seedocs/srmech/notes/continuous_math_as_14_class_cascade.md), so the module is simplycalculus.srmech.trigonometry(the trig subset) is unchanged. - Tests
tests/test_sqrt_rename_cascade_rc35.py(+6): sqrt/hypot vs libm + negative-domain raise + AST no-libm-sqrt guard;calculuscanonical surface;asymptotic_calculusis a true alias (identical callables +__all__).
Roadmap (per the derivation note): rc36 routes the ~32 math.sqrt/np.hypot sites onto the cascade + builds the direct-DFT (the Antikythera epicycle-sum, on cexp) + kron; rc37 QR + SVD; rc38 non-Hermitian eig + lstsq + einsum; the numpy→srmech[scientific] dependency-flip is the capstone. No new C symbols.
[0.7.0rc34] - 2026-06-04¶
"Continuous math is a cascade of the 14 A–N class operations" — the complex-exponential keystone, plus the derivation that dissolves the "scientific tier." Pure-Python additions; ABI stays 3; describe() 215 → 218 (+3 exp tools).
- New substrate-native exp family —
srmech.amsc.rational.{exp, cexp, complex_exp}(re-exported fromsrmech.asymptotic_calculus).exp(x)= Class-N Taylor with K argument-halving range reduction (e^x = (e^(x/2^k))^(2^k), no irrational constant);cexp(θ) = cos θ + i·sin θ= N(rc33 trig) ∘ C(imaginary-unit 90° rotation, Euler);complex_exp(z) = e^(z.real)·(cos z.imag + i·sin z.imag). Nomath.exp/cmath.exp/np.expin the call graph (AST-guarded). Matches libm to machine ε (cexp/complex_exp~3–9e-16; realexp~1e-9 absolute at e¹⁵, machine-ε relative). This is the keystone: everynp.exp(1j·…)time-evolution phase and every DFT twiddle factor IS acexp. - Derivation note
docs/srmech/notes/continuous_math_as_14_class_cascade.md— derives the A–N cascade for every op previously called "scientific-tier / numpy-only":exp(N∘K), trig (N∘I∘C∘K),complex-exp(N∘C),sqrt(N∘K), DFT/FFT (M∘{N∘C}∘I∘J∘K — the Antikythera epicycle-sum), QR (M∘C∘N∘K), SVD (L∘N∘K∘M, reachable fromhermitian_eigendecompose), non-Hermitian eig (K∘L∘{QR}∘C), lstsq ({QR}∘M∘I), kron (I∘M), einsum (B/D∘I∘M). Per the two-language MFO/srmech framework there are exactly 14 irrep class operations — so none of these is a primitive srmech lacks; each is a not-yet-derived composition. The §22 "scientific tier" framing is dissolved: numpy's only legitimate roles are the array container and a temporary fallback for not-yet-cascaded ops. - Tests
tests/test_continuous_exp_cascade_rc34.py(+6) — exp/cexp/complex_exp vs libm/cmath;cexp== Euler of srmech's own trig; AST guard against anymath/cmath/np .expin the call graph.
Roadmap (per the derivation note): rc35 promotes sqrt/hypot float wrappers + a direct-DFT cascade (on cexp) + kron; rc36 builds QR + SVD; rc37 non-Hermitian eig + lstsq + einsum; the numpy→srmech[scientific] dependency-flip is the capstone once cascade coverage is complete. No new C symbols.
[0.7.0rc33] - 2026-06-04¶
numpy-math → srmech-cascade routing: substrate-native trig (cos/sin/tan/atan/atan2) that replaces math/numpy trig at machine precision, plus Hermitian-eig routed onto srmech's own primitive across qm + signal_processing. Pure-Python additions; ABI stays 3; describe() 210 → 215 (+5 trig tools).
- New substrate-native float trig —
srmech.amsc.rational.cos / sin / tan / atan / atan2(also re-exported fromsrmech.trigonometryandsrmech.asymptotic_calculus). The exact Class-N Taylor cascades were always globally convergent; what was missing was the range-reduction wrapper that composes them with the π-cascade. The pipeline is: range-reduce the angle into [−π, π] using a high-precision π drawn frompi_cascade_digits(Archimedes hexagon-doubling — nomath.pi/np.piin the call graph), anchor to a Class-N rational at the float64 floor,cos/sinTaylor partial sum, then project the exact rational to float.atanuses a three-band argument reduction (√2∓1 edges). Measured vs libm: cos/sin ≈ 6e-16, atan/atan2 ≈ 2e-16 (machine ε). Class-K sign handling throughout (noabs()). - Trig call sites routed —
qm.sm(Weinberg / CKM angles), and thesignal_processingwindow/basis/rotation trig (cross_spectral,dct,multirate,multitaper,stft,ica_jade,form_function_rotation) now compute their trig through the cascade instead ofmath.cos/sin/np.cos/sin/arctan2, per the directive "never use numpy math when srmech can do it with a cascade." Value-faithful (per-site parity tests vs the prior libm result, ≤1e-9). - Hermitian eigendecomposition routed — every
np.linalg.eigh/eigvalshinqm.{potentials, gauge, single_particle, so8}andsignal_processing.{esprit, heat_kernel, ica_jade, music}now goes through srmech's ownamsc.laplacian.hermitian_eigendecompose(native C complex-Hermitian Jacobi when present; the eigenvalue/eigenvector math is srmech's, not LAPACK, on the native path). Real-symmetric inputs take a value-preserving.realon the eigenvectors; complex-Hermitian keep complex128. - Tests —
test_qm_cascade_routing_rc33.py,test_sp_eigh_cascade_routing_rc33.py,test_sp_trig_cascade_routing_rc33.py(eig parity ≤1e-9 + reconstruction, trig vs libm at machine ε, and each public op vs its pre-change output). The codebase-wideabs()AST ratchet stays green.
Scope: rc33 routes the audited qm + signal_processing numpy-math sites (docs/srmech/notes/numpy_math_abs_audit_rc32.md, Category B) onto srmech cascades. The genuinely scientific-tier numpy with no srmech equivalent yet (svd / qr / non-Hermitian-eig / lstsq / einsum / kron / complex-exp / FFT) stays per §22.
[0.7.0rc32] - 2026-06-04¶
numpy-optional core, step 3 — the real-symmetric Class-L Laplacian core is numpy-absent-safe (its eigenvalue math is srmech's OWN Jacobi cascade, never LAPACK), PLUS a codebase-wide abs() elimination (the rule: "abs() is never fine"). Pure-Python; ABI stays 3; describe() unchanged at 210.
amsc.laplacianreal-symmetric core (dense_adjacency/dense_laplacian/normalized_laplacian/jacobi_eigvals) now runs with numpy absent — the numpy import is guarded; numpy-absent builds returnlist[list[float]]andjacobi_eigvalsreturnslist[float]. The native C path (primary) is unchanged.jacobi_eigvals's no-native fallback is now srmech's OWN pure-Python Jacobi cascade (_jacobi_eigvals_py, a classical cyclic Jacobi rotation — the converged diagonal IS the spectrum), nevernumpy.linalg.eigvalsh(per the directive "never use numpy math when srmech can do it with a cascade"). It matches the native/numpy spectrum to Jacobi round-off (~1e-15). The complex-Hermitian / signed / magnetic ops stay scientific-tier (§22) and raise a clearImportErrorwhen numpy is absent.abs()eliminated codebase-wide (the absolute rule, value-preserving). Everyabs()/np.abs()/math.fabs()/sqrt(·²)stealth-abs in shipped srmech code is now an explicit Class-K sign-branch (x if x >= 0 else -x/np.where(x>=0, x, -x)) or real-imag composition (|z|² = z.real²+z.imag²,|z| = hypot(z.real, z.imag)) —amsc.rational/kepler/harmonics/laplacian/hypercomplex_dft,qm.bell/sm/pseudo_hermitian,spectral, and 13signal_processingDSP files (≈40 sites). A permanent AST ratchet (tests/test_laplacian_numpy_free.py::test_no_abs_calls_anywhere_in_srmech) fails CI if any realabs()call reappears anywhere insrmech/.tests/test_laplacian_numpy_free.py(new, +6) — the Jacobi cascade matches the numpy/native spectrum + the P4 closed-form spectrum; the real-symmetric build→eigvals chain runs with numpy monkeypatched absent; the scientific-tier ops raiseImportError; and the codebase-wide abs() ratchet.
Scope (honest staging): rc32 does the §22 Laplacian-numpy-free voxel + the absolute abs() sweep (value-preserving, low-risk). The codebase audit (recorded in docs/srmech/notes/numpy_math_abs_audit_rc32.md) also found ~26 numpy-math sites where srmech has a cascade (Hermitian np.linalg.eigh → hermitian_eigendecompose; np trig → Class-N rational trig); routing those is explicitly staged to rc33 (engine-swap — value-equal but a different code path needing per-site parity tests), not silently dropped. The genuinely scientific-tier numpy (svd/qr/non-Hermitian-eig/lstsq/einsum/kron/complex-exp/FFT) stays per §22. No new ops (describe() stays 210).
[0.7.0rc31] - 2026-06-04¶
Quaternion / octonion DFT cascade composites — quaternion_dft / octonion_dft (#863, F380). The native transform for a Klein-4 object: where a complex FFT collapses one of its two Z₂ chirality axes (the flat shadow), the QDFT's ℍ coefficient algebra resolves both. Pure-Python composites over the qm.octonion atoms; ABI stays 3; describe() 208 → 210.
Why it's exact, not analogical (F380): the Klein-4 group is the quaternion units modulo sign — Q₈/{±1} ≅ Z₂×Z₂. So the coefficient algebra of each FFT-ladder rung carries a different chirality content: complex FFT → ℂ → {±1,±i}/± = Z₂ (one axis); quaternion FT → ℍ → Q₈/± = Z₂×Z₂ (both axes). A QDFT's coefficient algebra matches the Klein-4 object's value algebra, so both axes survive.
cascade.quaternion_dft(x, *, form, mu_axis, inverse)—X[k] = Σ_n exp(σ·μ·2πkn/N)·x[n]. Left/rightform(ℍ is non-commutative → the twiddle can't be factored out as in the complex FFT; both forms round-trip).inverse(forward(x)) == xto float round-off, recovering all four components (both Z₂ axes). Composite over theqm.octonionleft/right-mult atoms restricted to ℍ.cascade.octonion_dft(x, *, form, mu_axis, bracketing, two_sided_right_axis, inverse)— the (8:7) rung. Carries the F378 non-associativity as an explicit declaredbracketingfield: the two-sided ODFTW_l·x·W_ris not unique, so(W_l·x)·W_rvsW_l·(x·W_r)must be stated — these measurably differ for octonions. One-sided forms round-trip; the two-sided form is forward-only (its inverse is open under non-associativity → raises).- Class home M (Clifford/HDC multiply) ∘ C (twiddle ±μ orientation) ∘ N (rational angle kn/N). No new primitive class; no
abs(). - Scientific tier (UPSTREAM §22): numpy is imported lazily inside each op, so
import srmech.amsc.cascadestays numpy-free (the rc30 numpy-absent-safe core is intact); the transforms use numpy on call (consistent with §22 keeping the python-side qm maths numpy-ful). tests/test_hypercomplex_dft.py(new, +16) — QDFT left/right round-trip (each μ axis); the load-bearing Klein-4 both-axes-preserved round-trip + the complex-FFT flat-shadow contrast (the complex projection drops bit1; the QDFT keeps it); ODFT one-sided round-trip; the two-sided bracketing is measurably non-associative; input validation; numpy-absent ImportError.cascade_catalog/{quaternion_dft,octonion_dft}.toml(new) — the DSL-catalog descriptors with left/right form, the octonion bracketing convention as an explicit attested field, and PDF-verified OA citations (Sangwine & Ell, arXiv:1001.4379; Błaszczyk, arXiv:1905.12631; Hahn & Snopek 2011, Bull. Polish Acad. Sci. 59(2):167–181 — the paywalled IEEE TIP 2007 / Elsevier 2017 papers were excluded in favour of their OA arXiv equivalents per the paywalled-DOI rule).
Tier note (the prototype/graduation split per #863): these are the prototype tier — composites over existing primitives, no capability gap. A graduation to a first-class native C primitive (srmech_quaternion_dft, like the existing fft) is a separate later voxel, explicitly deferred — not silently capped. Also folds a doc-honesty fix: the autocorrelation tool_schema summary's stale "numpy FFT fallback" claim (made inaccurate by rc30) now reads numpy-free. Full suite green.
[0.7.0rc30] - 2026-06-04¶
numpy-optional core, step 2 — the cascade layer is numpy-absent-safe (UPSTREAM §22, Option 1). The second voxel of the "runs embedded without numpy/LAPACK" framework-identity arc. Pure-Python; ABI stays 3; describe() unchanged at 208.
Introspection first (per the introspect-before-assert discipline): the four lowest-level core modules — srmech.amsc.cyclic (Class I), srmech.amsc.primes (Class J), srmech.amsc.rational (Class N), srmech.amsc.format (Classes A/B/the MPR IO) — were already numpy-free (zero import numpy). The only genuine numpy-absent crash sites in the cascade layer were two no-native fallback paths:
cascade.compose.autocorrelation— the no-native fallback usednumpy.fftto computeIFFT(|FFT(x)|²). Replaced with the defining direct circular-autocorrelation sumr[k] = Σ_n x[n]·x[(n+k) mod n](identical value for realx, no FFT / no numpy;math.fsumkeeps each per-bin sum well-conditioned). The native C path is unchanged and still primary whenHAS_NATIVE.-
cascade.atoms._try_native_chiral_dual— its float-list accel path did an unguardedimport numpy(the C callback marshals via numpy views). Now guarded: numpy absent → returnsNone→ the op's existing pure-Pythonchiral_dualfallback runs, rather than raisingImportError. -
tests/test_cascade_numpy_absent.py(new, +6) — the pure-Python autocorrelation matches both the closed-form definition and the numpy-FFT reference; asys.modules['numpy'] = Nonesimulation proves the cascade ops still run with numpy absent; and an audit-lock assertscyclic/primes/rational/formatcarry noimport numpy.
Scope note (the arc): numpy stays a hard dependency this rc — the flip to a srmech[scientific] extra is a later voxel (after qm/* + signal_processing get their ImportError guards). Explicitly deferred, not silently capped: the atoms stdlib-buffer C-boundary perf optimization (so the native chiral_dual accel path needs no numpy marshalling at all) is a tracked follow-up voxel — this rc only makes the path numpy-absent-safe, it does not yet make the native accel numpy-free. No new ops (describe() stays 208). No abs().
[0.7.0rc29] - 2026-06-04¶
numpy-optional core, step 1 — the HV carrier + the Klein-4 family goes numpy-free (UPSTREAM §22 / §22b, Option 1). The first voxel of the "runs embedded without numpy/LAPACK" framework-identity arc. Pure-Python; ABI stays 3; describe() unchanged at 208.
Per the RBS-LM research subtree's §22 recommendation (make numpy optional, for framework-identity — NOT as a reflex-fix), the boundary-type lever: the core A-N vocabulary returns a framework-native handle, not a raw np.ndarray (a numpy-typed return invites np.dot/np.linalg; a handle forces the srmech op).
- New
srmech.amsc.hv.HV— a numpy-free hypervector carrier over a stdlibarray('B')buffer. Plain-inthv[i]; scalar-boolhv == other(acceptsHV/ list /bytes/ 1-Dnp.ndarray, sonp.array_equal(rec, v)→rec == v);hv.tolist()/hv.tobytes()(stdlib) andhv.to_numpy()(opt-in bridge); buffer-protocol (the native C ops read/write it in place, so HAS_NATIVE keeps HV fast — pure-Python is only the no-native path). Imports no numpy at load. Distinct fromsrmech.spectral.SpectralHandle. - The whole Klein-4 family is now numpy-free internally + returns
HV—klein4_random(stdlib-randomseed path; the numpyrng=back-compat path is unchanged),klein4_bind/unbind/bundle/similarity(thesectors=/parallel=/mode=flag is preserved + value-identical; onlybundlechirality is value-meaningful), the threechirality_flip*/cpt_mirror,klein4_triality_cycle,klein4_holographic_encode/decode(rc27), andklein4_triality_encode/correct(rc28).klein4_sector_countreturns a stdliblist[int]. Noabs(). - Boundary plumbing: the MCP result serialiser coerces
HV → list(so MCP tool calls returning Klein-4 vectors still cross JSON-RPC);rbs_lm.substrate(a numpy research consumer, per §22) bridges back via the opt-in.to_numpy().
Scope note (the arc): numpy stays a hard dependency this rc — the dependency only flips to a srmech[scientific] extra in a later voxel, once every core module imports without numpy. qm/* + signal_processing keep numpy throughout (§22: "leaving numpy for the python-side triality/qm maths is correct"). Chosen for the framework-identity / embedded-install reasons, not as an agent-reflex cure (§22 honest caveat). Breaking: consumers of the klein4_* return type now get HV (use .tolist() / .to_numpy() / ==). Full suite green.
[0.7.0rc28] - 2026-06-04¶
Explicit order-3 triality-recursion corrector — klein4_triality_encode / klein4_triality_correct (#797 op (a1), F359 5-bar contract). The EXPLICIT k=3-CORRECT path past the order-2 4-cap (op (a2) is the measured no-Z3 substitute). Pure-Python; ABI stays 3; describe() 206 → 208.
The order-2 Klein-4 store is k=2-DETECT natively (F294: no Z3, 3∤4) — two views detect a mismatch but cannot say which is right. k=3-CORRECT needs the order-3 triality (τ³=I) past the 4-cap. The store carries the order-3 triality orbit of the value, [v, T(v), T²(v)] for T = klein4_triality_cycle, so the third vote IS the triality orbit's third element (T²v) — not an external 3rd render:
klein4_triality_encode(v)—len(v)*3uint8 store =[v | T(v) | T²(v)](orbit-major). Class-home M (orbit replication bind) ∘ I (the order-3 cycle that generates the orbit).klein4_triality_correct(store, *, depth=1)— brings every orbit-block back to the commonv-frame by inverting the triality (T⁻¹on block1,T⁻²=Ton block2) then takes the per-position 2-of-3 majority, correcting one error: k=3-CORRECT where the bare order-2 store is only k=2-DETECT.
The F359 5-bar contract (each falsifiable bar verified in tests/test_klein4_triality_corrector.py, new, +11):
1. blind correction beats the F353 holographic 0.25 baseline — single-error recovery is exact (measured rate 1.0);
2. the 3rd vote is the order-3 triality orbit of the same value (block2 == T²v, structurally);
3. C/Python parity — Python-first (co-equal); the standalone-C peer is the tracked next voxel;
4. disable the order-3 op → degrade to k=2-DETECT — without the inverse-to-frame step the raw orbit-blocks {v,Tv,T²v} disagree on every non-zero sector, so a naive majority does NOT recover v (correction is attributable to the triality, not to plain replication);
5. WIDTH-step only — one 4-cap crossing (order-2 → order-3); depth != 1 raises NotImplementedError rather than fabricating the continuum count-recursion (open math).
Both ops registered in tool_schema (describe 206→208). No abs(). Built to the #797-comment / F359 contract (the canonical §20/F359 figures are not on the read-only research branch). This closes the #797 op-pair: (b) directed/signed-Laplacian (rc26), (a2) holographic substitute (rc27), (a1) explicit triality corrector (rc28).
[0.7.0rc27] - 2026-06-04¶
Klein-4 holographic-erasure code — klein4_holographic_encode / klein4_holographic_decode (#797 op (a2), F353). The measured substitute for the (a1) triality corrector: k=3-CORRECT with NO Z3. Pure-Python; ABI stays 3; describe() 204 → 206.
The order-2 Klein-4 store is k=2-DETECT natively (F294: no Z3, 3∤4). k=3-CORRECT needs either the order-3 triality (op (a1), rc28) or this holographic-erasure route — replicate the store across replicas blocks so any one surviving replica-subregion (1/replicas) reconstructs the whole (the holographic "any subregion contains the whole" property at block granularity):
klein4_holographic_encode(v, *, replicas=4)—len(v)*replicasuint8 store (replica-major).klein4_holographic_decode(store, *, replicas=4, erased=None)—erased=mask→ first-surviving-replica (exact up to (replicas-1)/replicas = ¾ known-location erasure; raises if a position loses all replicas);erased=None→ per-position majority (blind, corrects up tofloor((replicas-1)/2)= ¼ errors at the default). These are the F353 measured tolerances.tests/test_klein4_holographic_erasure.py(new, +10) — ¾ known-erasure round-trip, any-single-block reconstruction, ¼ blind correction, and an honest past-capacity test (3-of-4 corrupted → decode is not claimed correct — no silent over-claim). Both ops registered in tool_schema (describe 204→206).
Class-home M (replication bind) ∘ C (surviving-copy / majority selection); no abs(). The standalone-C peer is the tracked next voxel. Built to the #797-comment / F353 spec. op (a1) (explicit order-3 triality corrector; F359 5-bar contract) follows in rc28.
[0.7.0rc26] - 2026-06-04¶
Two surfaces: (1) the srmech.asymptotic_calculus / srmech.trigonometry continuous-calculus import path now resolves; (2) the directed/signed-Laplacian eigen-op (Class L, #797 op (b)). Pure-Python; ABI stays 3; describe() goes 201 → 204 (the three new Class-L ops registered).
(1) srmech.asymptotic_calculus + srmech.trigonometry (new modules). The documented srmech.asymptotic_calculus.* import path had no module — the continuous-calculus primitives live in srmech.amsc.rational (Class N: sin/cos/exp/log1p/atan_series_truncate — exact-rational Taylor truncation, the substrate-native "continuous" trig) + the srmech/amsc/attested/asymptotic_calculus/ catalog. These thin re-export modules make the advertised path resolve (no regression — nothing was deleted; the import surface was simply never created). tests/test_asymptotic_calculus_alias.py pins the re-export identity.
(2) directed/signed-Laplacian (Class L, #797 op (b)). The undirected combinatorial Laplacian is the F348 navigation control (Fiedler shuffle-fragile r=0.214); two generalisations from the F347–F354 research:
signed_laplacian(n, edges, weights)— real-symmetric, PSD even with negative (frustrated) edges. The signed degreeD̄_ii = Σ_j |A_ij|is the Class-K magnitude of the signed-metric (the operation Spike #24 located as "Class O", DISSOLVED into Class L). Kunegis et al. (2010).magnetic_laplacian(n, edges, weights, *, q=0.25)— complex Hermitian; direction is encoded as a phaseexp(i·2π·q·(W−Wᵀ))so a directed graph stays Hermitian and the existing C-backedhermitian_eigendecomposediagonalises it — the complex eigenpair is the directed-navigation signature.q=0collapses to the real symmetrised Laplacian (the undirected control).fiedler_vector(matrix)— the λ₂ navigation embedding; dispatches real→symmetric_eigendecompose, complex→hermitian_eigendecompose(both C-backed).tests/test_directed_signed_laplacian.pypins the Hermitian/PSD/symmetry contracts + the q=0 control.
Cadence note (honest): the heavy eigendecomposition runs native today (the existing symmetric/hermitian C solvers), and the three new ops are registered in tool_schema (describe() 201 → 204; the no-carve-out coverage ratchet requires it). The only tracked next voxel is the standalone-C builder peers (srmech_graph_signed_laplacian / …_magnetic_laplacian) — mirroring the loop_bind Python-first→C-peer cadence (rc1 → rc7/rc20/rc21). Op (b) is the genuine new primitive (no substitute for directed navigation); op (a) (triality-recursion corrector / holographic-erasure substitute, #797) follows in rc27/rc28.
[0.7.0rc25] - 2026-06-03¶
CLI module docstrings refreshed to enumerate all four subcommands (status / bus / dsl / mcp) + an anti-staleness ratchet so they can't silently drift again (UPSTREAM_NOTES §13 D4). Docs only; ABI stays 3; describe() stays 201.
The srmech.cli package + srmech.cli.main module docstrings had frozen at the v0.5.0rc4 two-subcommand era (status + bus), silently omitting dsl (v0.5.0rc8) and mcp (v0.5.0) even after the live CLI grew to four. The user-facing argparse --help was already correct (refreshed v0.6.0rc12) — only the module docstrings drifted.
cli/main.py+cli/__init__.pydocstrings — now enumerate all four subcommands with their intro versions + Usage examples fordsl/mcp.tests/test_cli_docstring_freshness.py(new) — reads the live argparse subparser registry (no hard-coded list to itself go stale) and asserts both module docstrings and the top-level--helpdescription name every registered subcommand. A future subcommand that forgets the docstring now fails CI instead of drifting unnoticed.
This closes the last srmech-side item under the #855 "Dependency gates" list (§13 D4). It was a stale-finding cleanup: the behaviour was never wrong, only the module docs lagged.
[0.7.0rc24] - 2026-06-03¶
Class K real-axis atoms reject complex input cleanly — cascade.magnitude / cascade.pin_slot_at_zero now raise an intentional TypeError instead of leaking the internal x > 0.0 comparison (UPSTREAM_NOTES §15.1, srmech-side fix (b)). Pure-Python boundary guard; ABI stays 3; describe() stays 201.
cascade.magnitude and cascade.pin_slot_at_zero are Class K pin-slot operations — real-axis sign-splits, signature (x: float). A complex argument previously fell through to pin_slot_at_zero's if x > 0.0: and surfaced an opaque TypeError: '>' not supported between instances of 'complex' and 'float' — an internal comparison leaking, not a contract error.
_require_real(x, op)— shared boundary guard. RaisesTypeError("cascade.{op} is a Class K real-axis (pin-slot) operation and does not accept complex input … use e.g. math.hypot(z.real, z.imag) for the modulus.")before any dispatch. Catches Pythoncomplexand numpy complex scalars / 0-d arrays (dtype.kind == 'c'). Every real numeric (int/float/bool/Decimal/Fraction/ numpy real scalar) is ordered against0.0and passes through bit-identically — no behaviour change for in-contract inputs.- Honest class boundary: kept real-only (option (b), not (a)) because the complex modulus
(re**2+im**2)**0.5is a Euclidean-norm op — a different cascade class, not a Class K pin-slot. Conflating it would muddy the cascade-class accounting. - No C change / no ABI bump: the native
srmech_cascade_magnitude_f64/…_pin_slot_at_zero_f64symbols takec_double— the C ABI has no complex entry point by design, so this is purely a Python-boundary input-validation guard, not a new operation. The Rosetta "C = transpiled Python" discipline is not engaged (there is no Python operation here lacking a C peer; rejecting out-of-contract input is a language-level concern). tests/test_cascade_magnitude_complex_reject.py(new) — both atoms raise a cleanTypeError(with the actionable message) on Python complex and numpy complex128 / complex64; real inputs (float / int / negative / zero / NaN / ±inf) are unaffected.
With rc24 the §15.1 srmech-side gate (tracked in #855) is closed; the paired CLAUDE.md STOP-list doc correction lives on the parallel-session research branch (maintainer's to apply, per the upstream-as-research-notes discipline).
[0.7.0rc23] - 2026-06-03¶
parallel_sectors gains a body-kwarg channel — completes the §16.1 parallel_body= half so a kwarg-taking op can be a parallel body, symmetric with op=/.then. Pure-Python DSL; ABI stays 3; describe() stays 201.
rc22 made reorient an op=/.then/TOML stage (data-first); the §16.1 finding also named parallel_body=. Chain.parallel_sectors(body, *, n_sectors, combine) had no way to pass the body op's keyword-only options, so a required-kwarg op couldn't be a parallel_body:
Chain.parallel_sectors(body, *, n_sectors=4, combine="bundle", **body_kwargs)—functools.partial-binds**body_kwargsonto the body op before fan-out, so the bound body stays a unaryvalue → valuecallable and the per-sector dispatch is unchanged. e.g.chain.parallel_sectors("reorient", orientation=-1)/parallel_sectors("best_rational_signed", max_denominator=100).- TOML — the parallel branch forwards non-reserved stage keys (
[[stage]] parallel_body="reorient"+orientation=-1), mirroring theop=→then(**kwargs)path. tests/test_reorient_dsl_stage.py— extends with the kwarg-channel proof (bound orientation reaches the body) via Python + TOML.
Honest scope (no silent cap): the channel is the generic fix. reorient is a scalar op, so it's a valid parallel_body only at n_sectors=1 (the identity sector); at n_sectors≥2 the iω₇/γ₅ chirality stream-transforms iterate the stream per-element, which a scalar op can't consume (a category error, not a kwarg-channel failure). So the practical drivability for reorient remains the op= path (rc22); the channel benefits future kwarg-taking sequence bodies. With rc22 + rc23 the §16.1 dependency gate (tracked in #855) is closed.
[0.7.0rc22] - 2026-06-03¶
reorient is now data-first — reorient(value, *, orientation) — so it drives as a DSL chain stage (UPSTREAM_NOTES §16.1 fix (a)). BREAKING Python signature (the data arg moved first; orientation is keyword-only). C ABI unchanged (stays 3); describe() stays 201.**
reorient(orientation, value) was the one cascade-op whose data argument was second — every other stage-op is data-first. The DSL chain runner pipes the stream into arg 0, so op="reorient" bound the stream to orientation and dropped value (TypeError), and a stage orientation= kwarg collided on position 0. It was therefore un-invokable as an op= / .then() / TOML stage, though the catalog listed it kind="stage". Per the §16.1 "cleanest" resolution:
srmech.amsc.cascade.atoms.reorient(value, *, orientation)—valuepositional (the piped data),orientationkeyword-only. Now drives exactly likebest_rational_signed(x, *, max_denominator=…):chain.then("reorient", orientation=-1)in Python, or a TOML[[stage]] op="reorient"+orientation = -1(the non-reserved key is forwarded as the bound kwarg). C dispatch + Python fallback unchanged internally; the C ABI (srmech_cascade_reorient_{i64,f64}) is untouched.- Call sites updated (the order flip is breaking):
cascade.atoms(net_chirality),cascade.compose(best_rational_signed),cascade.parallel(_reorient_each/ iω₇ axis), thereorienttool_schemaToolEntry (params reordered),reorient.tomlsignature, and the cascade test suite. tests/test_reorient_dsl_stage.py(new) — pins the fix: reorient drives via.then("reorient", orientation=-1), composes aftermagnitude, and runs from a TOML chain; the old positional second-arg now raises (keyword-only).
Migration: reorient(o, v) → reorient(v, orientation=o).
Scope note (no silent cap): this fixes the op= / .then / TOML drivability (the primary finding). parallel_body="reorient" with a bound orientation is not yet supported — Chain.parallel_sectors(body, *, n_sectors, combine) has no body-kwarg channel, so any required-kwarg op (reorient's orientation) can't be a parallel_body; that body-kwarg forward is a separate follow-up. parallel_body ops whose kwargs all default (e.g. best_rational_signed) already work.
[0.7.0rc21] - 2026-06-03¶
Native C peers for the last three loop-bind ops computing via pure-Python — loop_associator / loop_left_op / loop_right_op. Closes a parity gap the rc20 #814 close glossed: those three (all named in #814's op spec) still ran the pure-Python Cayley-Dickson recursion (_loop_bind_raw) while cross7/g2_three_form already had dedicated C symbols. Now every op in the loop-bind spec is at native C/Python parity. Additive C symbols → ABI stays 3; describe() stays 201; every dispatch arm bit-exact with its Python fallback.
The "C = transpiled Python" Rosetta discipline (notebook §3.29.4–§3.29.5) admits no Python-only carve-out: a composition-op gets a C rendering exactly as cross7 = Im(bind) and g2 = ⟨x, cross7⟩ did (rc7). The associator (the Class-K residue, the genuinely-new k=7 arithmetic surfaced in #797/F271) and the L/R multiplication-operator matrices were the remaining pure-Python composites:
srmech_loop_associator_f64(c/src/srmech_loopbind.c) —(a·b)·c − a·(b·c), two fixed triple-products via the static octonion product (no recursion); 8 doubles out.srmech_loop_left_op_f64/srmech_loop_right_op_f64— the L_a (col k = a·e_k) / R_a (col k = e_k·a) operator matrices, n·n doubles row-major, byte-matching numpycolumn_stackof the per-basis binds.srmech.amsc.hdc—loop_associator/loop_left_op/loop_right_opdispatch to the peers for the dim-8 octonion whenHAS_NATIVE, pure-Python recursion kept as the Pyodide/WASM (and non-dim-8) fallback.tests/test_loop_operator_native_parity.py(new) — native peer vs the pure-Python recursion (the Rosetta agreement-attestation), plus the associator's known structure (zero on associative/Fano triples, antisymmetry).
With rc7 (per-block) + rc11/rc17 (HD bind SIMD) + rc20 (HD conj/inv/unbind/runbind) + rc21 (associator + L/R operators), the entire srmech.amsc.hdc loop-bind / Moufang surface is native at both single-octonion and HD-block scale — no op is Python-only. JPL Power-of-Ten clean (≤60-line, ≥2 asserts, no goto/malloc/recursion); warning-clean under -Wall -Wextra -Wpedantic -Werror / /W4 /WX.
[0.7.0rc20] - 2026-06-03¶
Native C peers for the rest of the HD loop family — completes "C = transpiled Python" (the Rosetta discipline, notebook §3.29.4–§3.29.5: one SSoT rendered as meaning : Python AND C source : Compiled C). The HD block conjugate / Moufang inverse / left-unbind / right-unbind were Python-only per-block loops; now each has a whole-array C transpile, so NO HD loop op is Python-only. Additive C symbols → ABI stays 3; describe() stays 201; every dispatch arm bit-exact with its Python fallback.
srmech_loop_bind_hd_f64 (rc11/rc17, the F292 #2 graft) gave the HD BIND its native peer; the companions (loop_conj_hd / loop_inv_hd / loop_unbind_hd / loop_runbind_hd) kept looping over 8-blocks in Python — a Rosetta with a glyph present in one script (Python) but missing from the other (C source), so those ops had no machine-code tier. This rc renders them in C too:
c/src/srmech_loophd_family.c(new) —srmech_loop_{conj,inv,unbind,runbind}_hd_f64, the SHIPPED per-block symbol (srmech_loop_conj_f64/srmech_loop_inv_f64/srmech_loop_bind_f64) applied over NB independent dim-8 blocks (the block-diagonal ⊕, #811/F289). The faithful transpile of the Python wrappers — same per-block ops, same order — collapsing the per-block Python loop into ONE native call (bit-exact with the fallback by construction).loop_inv_hdpropagatesSRMECH_ERR_BAD_INPUTfrom a zero block (→ the Python fallback raises, contract preserved). Scalar — no N-way SIMD here (the bind owns that,srmech_loopbind_hd.c); the heavy step where present (the product) is already native per-block.srmech.amsc.hdc—loop_conj_hd/loop_inv_hd/loop_unbind_hd/loop_runbind_hddispatch to the new peers whenHAS_NATIVE, with the per-8-block pure-Python recursion kept as the Pyodide / WASM fallback (hasattr-guarded ctypes, stale-.dll-safe).c/include/srmech.h— 4 new prototypes; the stale "the HD variants need NO C peer of their own" note is retired (it predatedloop_bind_hd's peer and contradicted the Rosetta discipline).tests/test_loop_hd_native_parity.py(new) — asserts the whole-array native peer agrees with the pure-Python Cayley-Dickson recursion (_loop_bind_raw/_loop_conj_raw) — the literal Rosetta agreement-attestation — plus the round-trip identities. The existingtest_loop_hd_division.pysuite now exercises the native path for free.
Closes the C/Python-parity ask in #814 (loop-bind op spec — "Parity-tested against the Python fallback", now true across the HD surface too) on the realization established in #811/#812 (the dim-8 → high-dim block-octonion ⊕). Each function is ≤60 lines, ≥2 asserts, no goto/malloc/recursion, single-line macros only, warning-clean under -Wall -Wextra -Wpedantic / /W4 /WX (JPL Power-of-Ten).
[0.7.0rc19] - 2026-06-03¶
HAL constant-attestation discipline — MPM (Mathematical Provenance Method) applied to CODE CONSTANTS, retroactive. Externally-sourced magic that srmech does NOT derive from its own framework (and historically transcribed by hand) is now attested in a per-target header MPR block AND, where derivable, regenerated-and-asserted by a test. Pure provenance/hardening: no behaviour change → ABI stays 3; describe() stays 201; every dispatch arm byte-identical.
Motivated by the rc18 SHA-NI K-table typo (0x8CC70808 where FIPS K[59] is 0x8CC70208) — a transcribe-from-memory error invisible on a host without the SHA feature, caught only on SHA-NI CI. The fix generalises: magic constants get attested + derived-and-asserted so the next such typo fails locally, on any host, at unit-test time.
c/src/srmech_sha256_constants.h(new) — the SINGLE attested home for FIPS K[64] + H0[8] (MPR block → FIPS 180-4 §4.2.2/§5.3.3). The three SHA-256 TUs (srmech_sha256.c/_batch.c/_shani.c) now#includeit instead of each carrying a hand-copied duplicate (that duplication was the same risk surface as the rc18 bug).c/src/srmech_sha256_shani.h(new) — the packed SHA-NIKP[16][2]+ an MPR block attesting BOTH the constants (FIPS) and the rnds2/msg1/msg2 instruction sequence (Intel SDM Vol 2 + Intel SHA Extensions whitepaper as primary; noloader/Walton as a working-impl pointer, NOT a drifting byte-hash; correctness pinned bytest_sha256_shani.pyon SHA-NI CI).c/src/srmech_simd.h— MPR block for the cpuid leaf/bit numbers (Intel SDM Vol 2A CPUID: leaf-1 ECX bit 27/28 OSXSAVE/AVX; leaf-7 EBX bit 5/29 AVX2/SHA; XGETBV XCR0 gate) + the GCC/Clang target-attribute feature strings (GCC x86 Function Attributes).tests/test_fips_constants_attested.py(new) — derive-and-assert: regenerates K from exact integer cube-roots of the first 64 primes (icbrt(p<<96)) and H0 from square-roots (isqrt(p<<64)) — no float, no ULP risk — and asserts byte-for-byte against the committedsrmech_sha256_constants.htables and the decoded packedKP. This is the gate that would have failed rc18's typo at unit-test time. (Self-check: asserts the derivation itself yields K[59]=0x8CC70208.)
The discipline going forward: a HAL/target magic constant ships with (a) an MPR attestation block in its .h, and (b) a regenerate-and-assert test where the value is derivable; a hand edit the test doesn't bless is a defect by construction.
[0.7.0rc18] - 2026-06-02¶
SHA-NI single-stream SHA-256 (F292 graft #3) — performance-engineering of srmech's OWN content-addressing hash for the common single-message case (sha256_bytes, every AMSC attestation). Built in CI so a SHA-NI-capable runner exercises the kernel, not parked because the dev CPU lacks the feature. New C symbol → ABI stays 3; describe() stays 201; JPL ratchet unchanged.
Graft #1 (rc10 sha256_batch) accelerates "hash N independent messages" by filling SIMD lanes; the single-message case it can't help is exactly the hot path (one response-bytes blob fingerprinted per attestation). The Intel SHA Extensions (SHA-NI) accelerate ONE message — _mm_sha256rnds2_epu32 runs two rounds per instruction, _mm_sha256msg1/msg2_epu32 drive the schedule — so one 64-byte block compresses in a handful of instructions.
srmech_sha256_shani(newc/src/srmech_sha256_shani.c) — FIPS-pads a block at a time, runs each block through the SHA-NI compress (state kept packed in the Intel ABEF/CDGH layout), and writes the raw 32-byte digest. The 64 rounds are factored into JPL-clean ≤60-line helpers (load warm-up + a cyclic steady-state group driven by a rotating message-register index + a final group) that are bit-identical to the canonical interleaving. A self-contained scalar duplicate (byte-for-byte thesrmech_sha256.ccompress, NIST-KAT-pinned) is the oracle AND the fallback.- HAL (
srmech_simd.{h,c}, rc11) gainssrmech_simd_has_shani()(leaf7 EBX bit29 — no OSXSAVE gate; XMM state is always OS-saved) +SRMECH_SIMD_TARGET_SHANI(target("sha,sse4.1,ssse3")on gcc/clang; empty on MSVC). Target-attribute-guarded compilation means the kernel builds on any host (incl. the SHA-NI-less dev CPU); runtime cpuid-dispatch enters it only where the feature is present. - Dispatch safety: the kernel is NEVER entered unless cpuid confirms SHA-NI, so the
SRMECH_SHANI_FORCE_TIERtest hook ({0,1}) can only select scalar-or-(SHA-NI-if-present) — it can never SIGILL.srmech.amsc.format.sha256_bytesprefers the SHA-NI peer when the rc18 symbol is bound (transparent hot-path accel; every arm bit-exact). - Honest coverage:
tests/test_sha256_shani.pypins the scalar oracle on every host (force tier 0) AND the auto path (kernel on SHA-NI runners, scalar elsewhere); the "kernel actually ran" assertion is exercise-if-present / skip-with-log keyed on_native.has_shani()(so a non-SHA-NI runner skips with a clear log rather than passing a scalar run off as kernel coverage). A CI cpuid-dump step records which matrix cells carry the feature._native.has_shani()surfaces the host capability (tri-state True/False/None).
Each function is pi-free, ≥2 asserts (the cpuid probe + the 1-line rotate are the documented Rule-5 exemptions), ≤60 lines, no goto/malloc/recursion, single-line macros only, warning-clean under -Wall -Wextra -Wpedantic / /W4 /WX.
[0.7.0rc17] - 2026-06-02¶
C-transpile of the last two rc12 chiral primitives (Class E + L) — closes the C/Python-parity gap so NO rc12 primitive op is Python-only (full-C-parity commitment per [[feedback_no_binding_layer_carveout]]). Additive C symbols → ABI stays 3; describe() stays 201; JPL ratchet unchanged.
rc16 transpiled the Class-I/D/G chiral ops; rc16's two "deferred" ops were a soft-MVP carve-out the project rejects — both have clean integer kernels and now have native C peers, dispatched-to when HAS_NATIVE (byte-exact Python fallback unchanged):
srmech_reverse_order(Class E →srmech_catalog.c) — the reversed index permutationout[i] = n-1-i(the chiral mirror of a sorted catalog; the wrapper applies the permutation to the(key, value)pairs); wired intosrmech.amsc.naming.reverse_order.srmech_three_fold_bands(Class L →srmech_laplacian.c) — the harmonic-3 three-fold band split (low/mid/highfromn/3+ remainder to the later bands so|low| ≤ |mid| ≤ |high|); wired intosrmech.amsc.laplacian.three_fold_eigvec_groups(the band-size computation gains a C path; the eigvec solve composes the existing Class-L spectral machinery).
Each is pi-free integer arithmetic, 2 asserts, ≤60 lines, no goto/malloc/recursion, warning-clean (-Wall -Wextra -Wpedantic). hasattr-guarded ctypes bindings (stale-.dll-safe). 4 new native-vs-Python parity tests (test_chiral_EL_c_parity.py) force both paths.
With rc16+rc17, every rc12 primitive op (D/E/G/I/L) now has a native C surface. (classify_harmonic is a static partition-constant lookup and classify_chirality_harmonic is a composite spectral reading — classifiers, not bare per-class primitives — so they compose rather than each get a C symbol, consistent with the primitive-vs-composite line the architecture already draws.)
[0.7.0rc16] - 2026-06-02¶
C-transpile of the rc12 chiral primitives — native C peers for the F150 Class-I/D/G chiral ops, byte-exact with their Python fallbacks. Additive C symbols → ABI stays 3; describe() stays 201 (no new public callable — the ops already shipped in rc12); JPL ratchet unchanged.
The three rc12 chiral ops with a clean integer/byte kernel now have a native C surface, dispatched-to when HAS_NATIVE (pure-Python fallback unchanged, byte-exact):
srmech_three_cycle(Class I →srmech_cyclic.c) — the Z/3 generator(value+1)%3, computed overflow-safe as((value%3)+1)%3; wired intosrmech.amsc.cyclic.three_cycle(uint64-range values dispatch to C, larger fall back to Python).srmech_mirror_pattern(Class D →srmech_dispatch.c) — the byte-reversed needle; wired intosrmech.amsc.dispatch.mirror_pattern.srmech_byte_search_backward(Class G →srmech_search.c) — the last-occurrence search (rfind; empty needle →len, absent →UINT32_MAX/None); wired intosrmech.amsc.search.byte_search_backward.
Each is pi-free integer/byte arithmetic, ≥2 asserts, ≤60 lines, no goto/malloc/recursion, warning-clean under -Wall -Wextra -Wpedantic (JPL Power-of-Ten). 9 new native-vs-Python parity tests (test_chiral_c_parity.py) force both paths and assert byte-exact agreement across boundaries / random sweeps / period-3 / involution / empty-absent-multi-occurrence. ctypes bindings are hasattr-guarded so a stale .dll falls back to Python rather than failing to load.
Deferred rungs (no-silent-caps): Class E reverse_order (list-of-pairs reversal — a Python data-structure op, not a numeric/byte kernel; intentionally Python-only) and Class L three_fold_eigvec_groups (eigendecompose-adjacent band split) remain Python-only for now; the rbs_lm Klein-4 encode helpers compose the already-C-backed klein4_* primitives.
[0.7.0rc15] - 2026-06-02¶
DSL surface audit — corrects stale op-counts in the LLM-facing cascade-DSL tool descriptions + adds an anti-drift guard. Descriptions only; no new ToolEntry → describe() stays 201; ABI stays 3; no C change.
The cascade-DSL tool descriptions had drifted behind the runner: the
srmech.dsl.list_catalog_ops ToolEntry summary enumerated only "8 ops"
(omitting the v0.7.0rc8 autocorrelation AND the v0.6.0 kuramoto_step /
parallel_sector_dispatch), and run_toml_chain referenced a "10-op cascade
catalog" — so an LLM driving the DSL via MCP would believe fewer ops exist than
the 11 that actually ship. The CLI (srmech dsl ops) was already correct
(it reads the catalog live).
- Corrected every stale count/list across the DSL surface: the two
_register_dsl_toolsToolEntry summaries (run_toml_chain/list_catalog_ops), thesrmech.dsl.__init__+srmech.dsl._tool_surfacemodule docstrings — all now cite 11 ops andlist_catalog_opsnames all eleven (autocorrelation,best_rational_signed,chiral_dual,chiral_flip,cyclic_gcd,kuramoto_step,magnitude,net_chirality,parallel_sector_dispatch,pin_slot_at_zero,reorient). - Anti-drift guard (
test_dsl_tool_surface_descriptions.py): the DSL ToolEntry summaries are now locked to the LIVElist_cascade_ops()set — every live op name must appear in thelist_catalog_opssummary and both summaries must cite the current op count, so a future op forces the descriptions to be updated (or CI fails) rather than silently falling stale.
[0.7.0rc14] - 2026-06-02¶
RBS-LM upstream wishlist — the srmech.rbs_lm inference substrate (UPSTREAM_NOTES §9; F166 walk). A NEW top-level module, pure-Python; outside the amsc.*/qm.* tool-schema enumeration → describe() stays 201; ABI stays 3; no C change.
Ports the F166 bit-exact, catalog-instantiable inference substrate from the research subtree into the package — inference built UP from the 28-D Klein-4 coordinate, not distilled DOWN from float weights. The typed substrate config of rc13 (substrate_parameterization) gets its first consumer.
srmech.rbs_lm.ContextSubstrate— the rolling-context encoder: per-token Klein-4 vectors (SHA-256 seed → fixed vector, sector arithmetic),iω₇position keys, odd-bundle of the last-k tokens → ONE Klein-4 state (Class A∘M encode + position). Plus the numpy-level encode helpers (token_seed,encode_word_k4,encode_bigram_l1,encode_skeleton_l2,encode_sentence_l3,sim_k4_batch) — the same Klein-4 sector cascade assrmech.amsc.hdc.klein4_*, at numpy-array granularity for the inference loop.srmech.rbs_lm.RBSLMInferenceSubstrate— the inference object:from_catalog(toml)/from_params(dict)build it;learn(stream)loads the bigram candidate structure + a context→next associative memory (F154-bounded,klein4_bindover windows, odd-bundle);next_token_distribution(ctx, temperature)retrieves over bigram-legal candidates (Class M) + temperature-softmax;infer(prompt, …, seed)is the deterministic autoregressive loop;attestation()returns the MPR block (descriptor_hash + srmech_version + abi + provenance) so any generated sequence is re-derivable.
Determinism: same corpus + params + srmech_version + seed → bit-exact identical output (SHA-256 token seeds, exact XOR bind, seeded sampling). Faithful (bit-exact) port of the research artifact; composes the existing srmech.amsc.hdc.klein4_* primitives + srmech.amsc.load_descriptor/descriptor_hash (no new hashlib.sha256 — routes through srmech.amsc.format.sha256_bytes via token_seed).
SCOPE / deferred rungs (no-silent-caps): rc14 ships the two classes. The srmech.rbs_lm tool_schema surface (LLM-as-tool inference) + the siona.profile("rbs_lm").infer() binding (§8+§9 join) + the hierarchical-memory scale-up (CanonicalHierarchicalMemory, F162 — past the single-memory F154 4× ceiling, for corpus-scale inference) remain open for follow-up rcs. [[feedback_no_mvp_framing]].
[0.7.0rc13] - 2026-06-02¶
RBS-LM upstream wishlist — the substrate_parameterization adapter + typed Descriptor sub-dataclasses (UPSTREAM_NOTES §7). Pure-Python, additive; no new ToolEntry → describe() stays 201; ABI stays 3; no C change.
A seventh AMSC adapter (html_scraper / json_api / csv_bulk / netcdf_grid / geotiff_bbox / literature_curated → + substrate_parameterization). Where the first six answer "where do the ground-proof rows come from?", this one answers "how is a parameterized substrate configured?" — every former module-level magic number of a substrate characterization run (the RBS-LM variable-length Klein-4 chirality-level sentence substrate is the canonical consumer) lives in attested [fetch.substrate_parameterization.*] sub-tables rather than script-embedded MVP magic ([[feedback_no_mvp_framing]] + user direction 2026-05-28).
- Typed sub-dataclasses (in
srmech.amsc.adapters.substrate_parameterization):SubstrateParams,EncodingParams,GenerationParams,HierarchicalParams,CorpusParams(required) +GrammarParams,PlausibilityParams,MeasurementParams(optional), composed into a frozenSubstrateConfig. First-class typed access (cfg.substrate.D) replaces dict-navigation (desc.fetch["literature_curated"]["substrate"]["D"]). parse_substrate_config(params)— validates the called-out invariants:D > 0(and every count);cycle_policy ∈ {forbid, allow, count_limited};corpus.source/corpus.tokenizer/grammar.modeenums;default_strategy ∈ allowed_strategies;0 ≤ plausibility weight ≤ 1;eps_smoothing > 0. Booleans are rejected where ints are required.config_for(descriptor)— typed accessor that locates the substrate sub-table insidedescriptor.fetch(thesubstrate_parameterizationkey when migrated, else the legacyliterature_curatedkey, else[fetch]directly), so it works for a migrated descriptor AND one still riding the interim adapter.fetch/parse— the AMSC adapter protocol:fetchreads the committed[fetch].ndjson_path(the characterization measurement output);parsedecodes it line-by-line. These rows are computed outputs attested by the descriptor +parser_rule_hash, so (unlikeliterature_curated) no per-rowsource_doiis required.
SCOPE: the typed config layer only. The run_substrate_characterization operation that consumes a SubstrateConfig + corpus + phase set and produces the measurement NDJSON is the substrate-module port (UPSTREAM_NOTES §9), landing in a follow-up rc. The adapter module lives under the coverage-exempt srmech.amsc.adapters.* prefix (like its six siblings), so no tool-schema churn. [[feedback_no_mvp_framing]].
[0.7.0rc12] - 2026-06-02¶
RBS-LM upstream wishlist — F150 chiral A–N harmonics (UPSTREAM_NOTES §6) + §2.2 cross-substrate alignment. 7 new ToolEntries → describe() 194→201 (+1 coverage-exempt utility); pure-Python, no C change (this rc), ABI stays 3.
Lands the research-side F150 framework move: the 14 A–N operators carry a per-operator chirality-harmonic order (½/3) that partitions the existing classes — H1 (chirality-invariant) = A B F H N, H2 (chiral inverse / mirror) = C D E G K M, H3 (chiral rotation / 3-cycle) = I J L. Variants land next to their base Class op (no privileged namespace, per [[feedback_no_privileged_primitive_classes]]; siona is a co-name alias, so these are srmech.amsc.*).
srmech.amsc.harmonics(new) —classify_harmonic(letter) -> 1|2|3(the static F150 partition) +classify_chirality_harmonic(hv) -> 1|2|3(spectral classifier: DC-dominant→H1, else zero-mean mirror vs 3-fold self-agreement→H2/H3; energy / inner-product ratios, no abs() on the substrate).HARMONIC_PARTITION+HARMONIC_LADDER_OPEN_RUNGSconstants.- Harmonic-2 mirror ops (period-2 involutions):
dispatch.mirror_pattern(D — byte-reversed needle),naming.reverse_order(E — order-reversed sorted catalog),search.byte_search_backward(G — last-occurrence search). - Harmonic-3 three-cycle ops (period-3):
cyclic.three_cycle(I — Z/3 generator; any non-negative int read mod 3, so the generic MCP int-synth is always in-domain),laplacian.three_fold_eigvec_groups(L — low/mid/high eigenvector bands). compose.greedy_bipartite_alignment(§2.2) — greedy cross-substrate kernel matcher (callersimilarity_fn); the Rosetta-layer utility. It takes a Python callable that cannot cross JSON-RPC, so it is not an MCP tool — it is coverage-exempt intests/test_tool_schema_coverage.py::_EXEMPT_FUNCTION_NAMESon the callable-arg rationale (public + tested, surfaced viasrmech.amsc.compose).- tool_schema: the 7 primitive ops are all registered and fully MCP-callable (
mcp_callable=True) — no handle-pending entries (the rc16 zero-handle-pending invariant holds):classify_harmonic(str),classify_chirality_harmonic(np.ndarray, JSON-list-coerced + flattened),mirror_pattern(bytes),reverse_order(list[tuple[bytes,bytes]]),byte_search_backward(bytes×2),three_cycle(int),three_fold_eigvec_groups(np.ndarray). →describe()194→201.
Ladder is staged, not capped (no-silent-caps): HARMONIC_LADDER_OPEN_RUNGS = {2: ("C","K"), 3: ("J",)} — Class M's H2 already ships as hdc.klein4_* (F132); C/K explicit mirror variants + J's speculative three_cycle_factor (F150 §6.3) remain open for a later rung.
SCOPE: framework-composition surfaces over the existing A–N primitives; most of UPSTREAM_NOTES §1/§2 (rfft, signed_sum_squared, symmetric_eigendecompose) was already shipped in prior rcs. [[feedback_no_privileged_primitive_classes]].
[0.7.0rc11] - 2026-06-02¶
F292 graft #2 — N-way SIMD block-octonion HD bind (srmech.amsc.hdc.loop_bind_hd), on a new SIMD optimize-path HAL (c/src/srmech_simd.h). NO new public callable → describe() stays 194; a NEW C symbol → ABI stays 3 (additive).
The on-theme F292 graft: loop_bind_hd is the block-diagonal direct sum ⊕ of NB independent dim-8 octonion products (F289 verified err 0.0) — exactly the data-parallel shape cpuminer's N-way-SIMD mindset exploits. It was a Python loop over the NB 8-blocks (one ctypes call per block); this collapses it to one native call that advances W blocks per SIMD pass.
srmech_loop_bind_hd_f64(x, y, nb, out)(c/src/srmech_loopbind_hd.c, new) — binds all NB blocks in one call via a runtime AVX (256-bit double, W=4) / SSE2 (W=2) / scalar dispatch. The SIMD kernels mirror the rc7mul2/mul4/mul8Cayley-Dickson op-DAG withdouble→ a vector holding W blocks of one component; the scalar tier (remainder / non-x86 / Pyodide) reuses the shippedsrmech_loop_bind_f64, so it is bit-exact with the single-block product by construction.loop_bind_hddispatches to it (whole NB-block array crosses once — no per-element Python loop); the per-block fallback is unchanged. NOTE the 256-bit double ops are AVX, not AVX2.- HAL —
c/src/srmech_simd.h+srmech_simd.c(new): ALL machine-specific bits other than the kernels now live in ONE place — theSRMECH_SIMD_X86platform macro, the arch intrinsic includes, theSRMECH_SIMD_TARGET_*per-function attributes, and the cpuid/xgetbv feature probes (srmech_simd_has_avx2/_avx/_sse2) + env-tier clamp (srmech_simd_tier). The portable core (srmech_sha256.c,srmech_loopbind.c) and the public header (c/include/srmech.h) stay 100% machine-agnostic. rc10'ssrmech_sha256_batch.cis retrofitted onto the HAL — its own platform/target/cpuid copies deleted (byte-identical SHA output, every tier; net FEWER Rule-5-exempt functions). New optimize-path ops include the HAL and write only their kernels. - Bit-exact, every tier: scalar / SSE2 / AVX all match the pure-Python
_loop_bind_rawper block (maxerr 0.00e+00, including the canonical HD width 2048 = 256·8) — the F292 "parity-trivial" prediction confirmed.tests/test_loop_bind_hd.py. SHA-256 batch regression: still byte-identical tohashlib+ NIST KATs at every tier. - JPL-clean: intrinsics (NOT asm); no
goto/recursion/malloc; ≤60-line functions; single-line vector macros (Rule 8); kernels self-isolate via__attribute__((target("avx"|"sse2")))so the library compiles at baseline ISA (no global-mavx); ≥2 asserts per non-exempt function (the cpuid probes are the documented exempt entries). gcc/clang/MSVC-Werror//WX.
SCOPE (load-bearing): energy/perf-engineering of srmech's OWN Class-M HD bind (octonion algebra — not hashing, not mining). The HAL is the architectural answer to "no machine-specific bits in the core; abstract the optimize path behind a header." [[feedback_trauma_informed_defensive_scope]].
describe() stays 194 (internal acceleration of an existing surface, no new ToolEntry); ABI stays 3 (new symbol, additive). Anchor: F292 (R-RBS-LM-FINDING_292_cpu_optimization_reference_graft_handdown).
[0.7.0rc10] - 2026-06-02¶
F292 graft #1 — N-way SIMD SHA-256 BATCH (srmech.amsc.format.sha256_batch), folding the F292 CPU-optimization hand-down into v0.7.0. +1 ToolEntry → describe() 194; a NEW symbol → ABI stays 3 (additive).
The first "apple-tree" graft from F292: take cpuminer's battle-tested N-way SIMD SHA-256 technique (sha256d_ms_4way/8way) and re-implement it JPL-clean in srmech's own hash, for the bulk-attestation common case (fingerprinting a whole catalog of upstream response bytes at once).
srmech.amsc.format.sha256_batch(datas) -> list[str]— one 64-char lowercase hex digest per message, each byte-identical tosha256_bytes(d)/hashlib.sha256(d).hexdigest(). A throughput surface, NOT a new content-address shape. Dispatches to the native peer when present, else ahashlibloop.srmech_sha256_batch(c/src/srmech_sha256_batch.c, new) — a runtime cpuid dispatch to AVX2 8-way / SSE2 4-way (scalar fallback for then mod Wremainder, non-x86, and Pyodide). The W lanes step through their own message's 512-bit blocks in SIMD lockstep, with a per-lane mask freezing a lane once its (shorter) message is done — so variable-length batches are correct, each lane's state advancing exactly as the scalar one-shot would.SRMECH_SHA256_FORCE_TIER={0,1,2}overrides the dispatch (test hook).- Bit-exact, every tier: scalar / SSE2 / AVX2 all match
hashlib+ the NIST KATs ("","abc", 1M-a) over a full padding-boundary length matrix and mixed-length batches (verified locally via the force-tier hook; CI's native cells exercise the host tier).tests/test_sha256_batch.py. - JPL-clean: intrinsics (NOT asm); no
goto/recursion/malloc; ≤60-line functions; the SIMD sigma ops are single-line macros (Rule 8); the AVX2 kernel self-isolates via__attribute__((target("avx2")))so the library compiles at baseline ISA (no global-mavx2); ≥2 asserts per non-exempt function (the cpuid feature-detectors are the documented exempt entries). gcc/clang/MSVC-Werror//WX.
SCOPE (load-bearing): energy/perf-engineering of srmech's OWN provenance hashing — NOT cryptocurrency mining (binding doesn't make hashing cheaper; SHA-256 has no PoW shortcut; "a correct instrument, not a money printer"). Technique attested to public references (FIPS 180-4 for the algorithm; the Intel Intrinsics Guide + Gueron & Krasnov, "Parallelizing message schedules to accelerate SHA-256" for the N-way structure); cpuminer (GPLv2+, forward-compatible with srmech GPL-3.0+) was read only as a working-impl pointer. [[feedback_trauma_informed_defensive_scope]].
+1 ToolEntry → describe() 193 → 194; ABI stays 3 (new symbol, additive). Anchors: F292 (R-RBS-LM-FINDING_292_cpu_optimization_reference_graft_handdown); the btc-rosetta midstate bench (the measured 1.73× energy anchor).
[0.7.0rc9] - 2026-06-02¶
MS #21 rc9 voxel — the v0.7.0 graduation-prep PyPI description refresh (the genuinely-last rcN before the clean v0.7.0 cut). Description-only → describe() stays 193; DSL catalog stays 11 ops; ABI stays 3.
The PyPI Summary predated the v0.7.0 arc — it named "octonion-multiplications" and "Spin(8) triality" but not the Moufang loop-bind op family the arc actually shipped, and nothing of rc8's autocorrelation. This voxel refreshes it (no code change):
- Names the v0.7.0 headlines: Moufang loop-bind, 7-D cross product, G_2 3-form (the octonion family, rc1–rc7) and Wiener-Khinchin autocorrelation (rc8) join the cascade-parity list (Kuramoto too — shipped at v0.6.0 but never surfaced in the summary).
- Preserves the substrate-native spine verbatim in substance: 28-dim chiral hyper-loop = so(8) adjoint (14 g_2 derivations + 14 L/R octonion products; Spin(8) triality), made hardware-callable.
- Trimmed the redundant
dispatch, catalog, templating, Kepler+dual-path signal-processingtail (covered by "Full cascade-catalog C/Python parity") → 472 chars, under the 480 soft / 512 hard PyPISummarylimit. Byte-identical inpyproject.toml+pyproject-pure.toml(the publish-workflow drift guard).
Description-only — no code touched, so describe() stays 193, the DSL catalog stays 11 ops, ABI stays 3. This is the final rcN; the clean v0.7.0 graduation to production PyPI follows (human-gated).
[0.7.0rc8] - 2026-06-02¶
MS #21 rc8 voxel — the Class-L circular autocorrelation primitive (the F290 §C un-flatten Wiener-Khinchin op), shipped CO-EQUAL in Python AND C. +1 ToolEntry → describe() 193; +1 cascade-catalog op → 11 DSL ops; new symbol → ABI stays 3.
The F290 §C "un-flatten" catalog composite (autocorr → difference-graph → conservation-validate) was blocked on one missing Class-L primitive: srmech had no autocorrelation op, so the composite could not be authored as pure-TOML over named ops. This voxel ships it, Python + C together:
srmech.amsc.cascade.autocorrelation(x)— the circular autocorrelationr[k] = Σ_i x[i]·x[(i+k) mod n](r[0] = Σ x² = energy) of a real sequence. This is EXACTLY the Wiener-Khinchin spectral objectr = Re(IFFT(|FFT(x)|²))(the circular-convolution theorem) — that identity is WHY it is Class L (the spectral side: autocorrelation ↔ power spectrum). The Python wrapper computes it the fast way (numpy FFT).n==0 → [].srmech_autocorrelation_f64(c/src/srmech_autocorr.c) — the co-equal native peer computes the DIRECT O(n²) multiply-add sum — the IDENTICAL object, and JPL-clean: no FFT, hence no recursion (Rule 1) and no transcendentals — just bounded loops over caller buffers, so it runs on a microcontroller with no host Python and no FFT library.srmech.amsc.cascadedispatches to it whenHAS_NATIVE, else the numpy FFT fallback. Parity to FFT round-off (~1e-12, NOT bit-exact — the FFT route and the direct sum accumulate in different orders; a compiler may also contracta*binto an FMA).- HONEST CASCADE SHAPE: Class L (the Wiener-Khinchin reading); computationally a Σ-reduce of products — no
abs(), no sign branch. NOT a new privileged primitive class. autocorrelation.tomldescriptor (class_composition = "L",c_symbol_f64 = "srmech_autocorrelation_f64") makes it discoverable (srmech dsl ops→ 11 ops) and runnable as a DSL stage.tests/test_autocorrelation.py: the FFT route equals the naive direct-sum definition (the spectral identity holds); the energy anchorr[0] == Σ x²; circular symmetryr[k] == r[n-k]; boundary cases (n==0 → [],n==1 → [x[0]²], constant signal); catalog discovery; and (native) the direct-sum peer matches the naive sum to~1e-12.
JPL Power-of-Ten clean (one ~13-line function, ≥2 asserts, no recursion/malloc/goto; gcc/clang/MSVC -Werror//WX). +1 ToolEntry → describe() 192 → 193; ABI stays 3 (a new symbol is additive). Unblocks the F290 §C un-flatten composite as pure-TOML over named ops. Anchors: Wiener 1930 / Khinchin 1934 (the autocorrelation ↔ power-spectrum theorem); R-RBS-LM F290 §C (the un-flatten catalog).
[0.7.0rc7] - 2026-06-02¶
MS #21 rc7 voxel — the co-equal C peer for the octonion loop-bind family (the Python→C transpile). New native symbols → ABI stays 3 (additive); describe() stays 192.
The MS#21 loop-bind family shipped Python-first (rc1–rc6); this voxel lands its co-equal Compiled-C tier so srmech runs the octonion algebra natively (the microcontroller-readiness commitment). c/src/srmech_loopbind.c ports the dim-8 octonion (Cayley-Dickson) product and companions:
srmech_loop_bind_f64— the octonion product(a,b)(c,d) = (a c − conj(d) b, d a + b conj(c)). JPL Rule 1 bans recursion, so the Python's recursive_loop_bind_rawis unrolled as a fixed real→complex→quaternion→octonion call DAG (srmech_loop__mul2/4/8) — bit-exact with the Python (identical operand order at every level).srmech_loop_conj_f64(Class-C conjugate),srmech_loop_inv_f64(Moufang inversex̄/⟨x,x⟩; Class-K clean),srmech_cross7_f64(Im(loop_bind)),srmech_g2_three_form_f64(⟨x, cross7(y,z)⟩).- Octonion carrier only: every
nmust be 8; other dims returnSRMECH_ERR_BAD_INPUTand the Python keeps its recursive fallback. The HD block variants (loop_bind_hd,loop_unbind_hd,loop_conj_hd,loop_inv_hd,loop_runbind_hd) inherit native acceleration for free — their wrappers loop over 8-blocks calling the per-blockloop_bind/loop_conj, which now dispatch to C. - Python dispatch in
srmech.amsc.hdc:loop_conj/loop_bind/loop_inv/cross7/g2_three_formtry the native path (type/size-guarded;n==8only) then fall back to pure Python — exact behaviour preserved. tests/test_loopbind_parity.py: native == pure-Python — exactarray_equalforloop_conj(pure negation) + the dim-16 sedenion fallback;<1e-12for the multiply-bearingbind/inv/cross7/g2/HD-per-block (a compiler that contractsa*b−cinto an FMA, e.g. clang on macOS, may differ ≤1 ULP); plus octonion identitiesx·x⁻¹=e₀+ cross7 antisymmetry.
JPL Power-of-Ten clean (≤60-line functions, ≥2 asserts each, no recursion/malloc/goto; gcc/clang/MSVC -Werror//WX). No new ToolEntry / no new class — these are native peers of existing ops, so describe() stays 192; ABI stays 3 (new symbols are additive). Verified bit-exact on a 64-bit local build over 500 random octonions; CI's native cells + the TestPyPI wheel are the cross-compiler gate.
[0.7.0rc6] - 2026-06-02¶
MS #21 rc6 voxel — bring-your-own (BYO) cascade-TOML (#811 / F289 D2). Config API only → describe() stays 192; ABI stays 3.
The DSL cascade-catalog was a closed set: only the 10 shipped *.toml descriptors under srmech/amsc/_research/cascade_catalog/. This voxel opens it — a domain specialist who needs a cascade srmech doesn't catalog, or a behaviour defined by a TOML descriptor that follows srmech's naming, can now bring their own:
srmech.dsl.register_catalog_dir(path)— register an external dir of*.tomlcascade descriptors. The zero-API equivalent is theSRMECH_CASCADE_PATHenv-var (os.pathsep-separated dirs). Registered ops then resolve (chain().then(...)/run_toml_chain), run, and surface (list_catalog_ops/srmech dsl ops) identically to shipped ops.- PURE-TOML composites — a user descriptor may carry a
[composite]body whose[[composite.stage]]array is a chain of named ops (no Python):lookup_cascade_opresolves it to a unary stage that builds + runs the sub-chain. (Or a primitive descriptor, which needs a matchingsrmech.amsc.cascadecallable, exactly as the shipped ones do.) - MPM provenance tiers — every descriptor is tagged
_provenance: shipped = "srmech" (A-tier); user = "user:<sha256>" (B-tier, attested to the user's own descriptor hash, NOT a shipped primitive).list_catalog_ops()gains a"provenance"field ("srmech"/"user"). - Loud-at-load validation — a user op-name may not shadow a shipped or earlier op (raises); composites are validated at load (every referenced op resolves; the composite graph is acyclic). A typo fails loudly at load, not silently at run — the "follow srmech naming" gate.
tests/test_byo_cascade_toml.py(9 tests): register + resolve + run a user composite (fluent builder +run_toml_chain);SRMECH_CASCADE_PATH; provenance tags; shadow-rejection; unknown-op / cycle / missing-name loud-at-load; nonexistent-dir rejection; shipped catalog +describe()unchanged.
Config API only — register_catalog_dir is not a cascade op / not a ToolEntry, so describe() stays 192; ABI stays 3 (pure-Python, additive). Defaults blessed by the user (reject-on-shadow protects MPM A-tier integrity; ship anchors then iterate from use). Anchors: F289 D2 (rc4_handdown_and_byo_cascade_toml); §12.3 confirmed-deferred.
[0.7.0rc5] - 2026-06-02¶
MS #21 rc5 voxel — the per-block HD Moufang-division family + the loop_inv/loop_conj HD footgun guard (F-§12.1 / §12.2). +3 ToolEntries → describe() 192; ABI stays 3.
rc4 lifted the bind to HD per-block; the unbind/conjugate atoms it leaned on were still single-element. A bug-test sweep (upstream §12) caught the gap: loop_inv / loop_conj operate on ONE Cayley-Dickson element, but 2048 = 256·8 is also a power of two, so an HD block-octonion vector silently passed _as_loop and got treated as one giant 2048-D element — the natural loop_bind_hd(E, loop_inv(c)) was off by ‖·‖≈16 with no exception. This voxel closes that and completes the HD division family:
srmech.amsc.hdc.loop_conj_hd(x)— the missing per-block conjugate atom: the direct sum ⊕ of NB independent dim-8loop_conjs. Class C over the direct-sum TILE layout; NO new class.srmech.amsc.hdc.loop_inv_hd(x)— per-block Moufang inverse (x̄ₖ/⟨xₖ,xₖ⟩per block); the per-block unbind key. Class-K clean (per-block norm² gate, neverabs()).srmech.amsc.hdc.loop_runbind_hd(a, b)— the HD RIGHT-unbind (per-blockbₖ·conj(aₖ)). Whereloop_unbind_hdpeels the LEFT factor, this peels the RIGHT — recoversvfromloop_bind_hd(v, a)exactly ((vₖ·aₖ)·conj(aₖ)=vₖby alternativity; verified recovery<1e-15). Right-division is what a left-fold sequence store(((s₀·s₁)·s₂)…)needs to peel the most-recent element off the right (F-§12.2).- Footgun guard (F-§12.1):
loop_inv/loop_conjnow raise on an HD block-octonion input (a multiple ofLOOP_DIM=8wider than one octonion), pointing at the*_hdop — loud failure replaces the silent-wrong global result. The single-octonion path (dim ≤ 8) is unchanged. tests/test_loop_hd_division.py: per-block conj/inv = the shipped single-element op block-wise;loop_inv_hd == loop_conj_hdon unit blocks; right-unbind round-trip; theloop_inv/loop_conjHD guard raises; the single-octonion path still works; multiple-of-8 validation.
+3 ToolEntries (189 → 192); ABI stays 3 (pure-Python, additive). The co-equal C peer is the arc's transpile-to-C step (Python-first ladder). Anchors: upstream §12.1 / §12.2 bug-test hand-down.
[0.7.0rc4] - 2026-06-02¶
MS #21 rc4 voxel — the block-octonion HD tiling (#811) + capacity-free vs Klein-4 (#812). +2 ToolEntries → describe() 189; ABI stays 3.
The fourth v0.7.0 voxel lifts the dim-8 octonion loop-bind to hyperdimensional width, all ground-truth computed from the shipped loop_bind (so it agrees with rc1 by construction; F289):
srmech.amsc.hdc.loop_bind_hd(x, y)— the block-octonion HD bind:D = NB·8(canonical 2048 = 256·8) bound block-wise = the direct sum ⊕ of NB independent dim-8 Moufang binds. Block-DIAGONAL — block k of the result is exactlyloop_bind(x_k, y_k); nothing couples blocks (verified err0.0e+00). Class M (per-block loop_bind = M∘C with a Class-K residue) over a direct-sum tile layout — NO new class.srmech.amsc.hdc.loop_unbind_hd(a, b)— per-block Moufang left-divisionconj(a_k)·b_k; recoversvfromloop_bind_hd(a, v)for unit-per-blocka(verified err2.9e-15). Class-K clean (conjugate + bind, noabs()).- Capacity-free vs Klein-4 (owned verdict, F289/F277): at matched
D=2048the loop-bind's bind/unbind retrieval capacity is ≥ Klein-4 (identical through K=64; loop ≥ klein4 at K=128) — so it carries order + tree + direction (F274) at no capacity cost vs the commutative XOR bind. (Honest scope: the K=128 edge is one regime, not a general advantage; the load-bearing claim is the null cost.) tests/test_loop_bind_hd.py(7 tests): block-diagonal err 0.0, block independence, per-block product = the shipped Cayley–Dickson table, unbind recovery < 1e-12, multiple-of-8 validation, the capacity-retrieval mechanism.
+2 ToolEntries (187 → 189); ABI stays 3 (pure-Python, additive). The co-equal C peer is the arc's transpile-to-C step (Python-first ladder). Anchors: F289 (rc4_groundtruth.py); capacity curve F277.
[0.7.0rc3] - 2026-06-02¶
MS #21 rc3 voxel — the loop-bind family slots into the compose engine (#813). Test-only proof; describe() stays 187; ABI stays 3.
813 asks how the octonion loop-bind composes in srmech's operator-chain surface. The answer needed no new code: DEFAULT_CLASS_REGISTRY["M"] → srmech.amsc.hdc and ops resolve dynamically by name, so loop_bind / loop_conj / loop_associator / cross7 / g2_three_form already run as class="M", op="<name>" steps through srmech.amsc.compose.run_chain — the M∘C-with-K-residue cascade #813 describes.¶
tests/test_loop_bind_compose.py(4 tests): single-steploop_bind/loop_associator(the K residue) /cross7/g2_three_formall resolve + run viarun_chain; a two-step M∘C chain (loop_bindthenloop_conjvia@step[0]) proves multi-step composition.
Test-only voxel: NO new ToolEntries (describe() stays 187), NO new class, ABI stays 3 (pure-Python). The formal cascade-catalog .toml descriptor for the bind carries a [cascade.native] C symbol, so it lands with the C-transpile step at the end of the v0.7.0 arc (Python-first → transpile-to-C ladder). The user-authored / bring-your-own external cascade-TOML path is a separate scoped voxel.
[0.7.0rc2] - 2026-06-02¶
MS #21 rc2 voxel — the 7-D cross product + the G₂ associative 3-form (#813 / F281). +2 ToolEntries → describe() 187; ABI stays 3.
The second v0.7.0 voxel adds the loop-bind's companion invariants, both with ground-truth computed from the shipped loop_bind (so they agree with the rc1 bind by construction — no convention guess; F281):
srmech.amsc.hdc.cross7(x,y) = Im(loop_bind(x,y))— the 7-D cross product (antisymmetric; for imaginary x,y= ½(xy−yx)). Class M∘C (bind ∘ imaginary-part ordering). Identity‖x×y‖²=‖x‖²‖y‖²−⟨x,y⟩².srmech.amsc.hdc.g2_three_form(x,y,z) = ⟨x, cross7(y,z)⟩— the associative calibration 3-form; nonzero ±1 on exactly the 7 Fano associative 3-planes, 0 on the other 28 of C(7,3)=35. Class (M∘C)∘⟨·,·⟩.- Triality verdict (owned; F281):
tests/test_cross7_g2_three_form.pyassertsdim Der(loop_bind) == 14(= G₂) and that a generic O(8) rotation breaks the bind ⟹ triality does NOT preserve the bind; the 14-dim G₂ does. (klein4_triality_cycleis the V₄-sector carrier, co-resident — not a bind-automorphism.)
NO new class (the 14 A–N hold; Class O stays dissolved). The #813 compose-engine registration is deferred to rc4 (lean discipline). Citations: Baez 2002 (7-D cross product / G₂); Harvey–Lawson 1982 (calibration 3-form). +2 ToolEntries (185 → 187); ABI stays 3 (pure-Python, additive).
[0.7.0rc1] - 2026-06-02¶
MS #21 loop-bind (Moufang) voxel — the k=7 gauge ARITHMETIC the triality symmetry is blind to (#814 / F271). Pure-Python core in srmech.amsc.hdc; +6 ToolEntries → describe() 185; ABI stays 3.
The first v0.7.0 voxel: srmech gains the octonion product (the gauge arithmetic) beside the triality automorphism it already had (the gauge symmetry). Ported faithfully from the loop_bind_moufang.py research oracle (F271/F272) as M∘C with a Class-K associator residue — NO new class (the 14 A–N hold; Class O stays dissolved); structure = the Moufang loop.
srmech.amsc.hdc.loop_bind— the Moufang / Cayley-Dickson octonion product (non-commutative + non-associative ⟹(ab)c ≠ a(bc), the (4:3)|(3:4) chirality);loop_conj(conjugate);loop_inv(Moufang-division unbind,x̄/⟨x,x⟩);loop_left_op/loop_right_op(L/R = the order chirality);loop_associator(the Class-K residue(ab)c − a(bc), zero on a Fano line). Class-K clean (norm², noabs()). rc1 is the dim-8 octonion core (division holds); the block-octonion HD tiling (#811), the co-equal C peer, and the triality-automorphism composition check (#813) are later voxels.tests/test_loop_bind_moufang.py(8 tests) reproduces the F271/F272 numerics: 7 associative Fano lines,[L_a,R_b]·x = −associator, the three Moufang identities, Jacobi-fails/Mal'cev-holds, the inverse unbinds, Artin associativity, e₀ identity.
Canonical SSoT: Baez, J.C. (2002) "The Octonions", Bull. Amer. Math. Soc. 39, 145. +6 ToolEntries (179 → 185). ABI stays 3 (pure-Python, additive). JPL audit ratchet unchanged.
[0.6.0] - 2026-06-01¶
Production graduation of the v0.6.0 rc1–rc21 lean-ISA voxel arc to PyPI. The clean (non-rc) tag promotes the rc21 state already verified-green on TestPyPI — the only delta from rc21 is this version string + entry, and the full pedantic-C (gcc/clang/MSVC) + 4-cell test matrix + pure-wheel build re-verify the 0.6.0 build before the production tag. ABI 3; describe() total 179.
The arc, voxel by voxel:
- Lean-ISA two-tier split (#751) —
cascade.atoms/cascade.compose: a finite anharmonic KERNEL (14 A–N primitives + the five Bird-Meertens combinatorsthen/loop/fold/reduce/parallel) vs an asymptotic TOML CONTINUUM of cascade instances ("you can't hardcode a continuum"). - 𝔰𝔬(8) / triality engine —
srmech.qm.so828-generator adjoint (14 g₂ + 7 L + 7 R);srmech.qm.trialityorder-3 outer automorphism withFix(τ) = g₂ = 14;quaternion_subalgebra_stabilizerso(4)=su(2)⊕su(2) (#759);lean_isa_seventh_primitive(#761). - Reentrant C core (#772); the Klein-4 four-sector
cascade.parallel_sector_dispatchPython surface (#778) + co-equal C peersrmech_cascade_parallel_sector_dispatch(#771), made chainable/nestable with acombine=recombine; the DSLparalleldiscriminator +[cascade].kindstage/combinator classification. - Generalised Kuramoto-Sakaguchi step —
cascade.kuramoto_step(…, adjacency=, alpha=, pin_anchor=, pin_strength=)shipped CO-EQUAL Python + standalone C (srmech_cascade_kuramoto_step_general_f64); theklein4_*HDC ops gained asectors=/parallel=/mode=flag. - The rc16–rc21 triality voxel sub-arc — combinator-kernel-closure ratification (rc16) →
klein4_triality_cyclePython op (rc17) + co-equal C peersrmech_klein4_triality_cycle(rc18) → continuum-tier worked instancetriality_s3_klein4.toml(rc19) → SSoT two-tier coherence-ratchet scan (rc20) → MFO §VII.6.22 H-gate/triality rung (rc21).
No code change from rc21; version-string graduation + this entry only.
[0.6.0rc21] - 2026-06-01¶
MS #20 H-gate / triality MFO rung voxel (the meaning-tier closer) — MFO notebook §VII.6.22 connects the rc16–rc20 triality voxel-arc to the §VII.6.21 Rosetta-table H-gate / fix-rotate axis. DOC only (research notebook); no code, no new symbol/ToolEntry; describe() stays 179; ABI stays 3.
The SSoT-coherence closer of the arc, at the meaning tier: rc16 named the two-tier boundary, rc17/rc18 shipped the klein4_triality_cycle op (Python + co-equal C), rc19 the worked instance, rc20 the coherence ratchet; rc21 reads the whole voxel back into the MFO canon as a building block (per user direction 2026-06-01) — a starting block for downstream review, refactored back if usage finds misfits.
docs/antikythera-maths/mfo_spectral_research_notebook.md§VII.6.22 — "The triality cycle is the executable rotate-operator whose fixed point IS the frame-invariant." It reads the rc16–rc20 voxel-arc as the executable instance of §VII.6.21.4: the order-3klein4_triality_cycleT (rc17 Python + rc18 C) is the discrete-cyclic rotate-operator; its continuous-Hopf companionsrmech.qm.trialityτ fixesg₂ = 14(the A–N core; the §VII.6.21.4 frame-invariant);klein4_bind(XOR concord) is the fix-frame / agreement;klein4_similarityis the H = measurement gate. The discrete triality CLOSES exactly (T³ = id, no Class-N rational-anchor leak) where the continuous epicycle leaks into the hidden fiber — the two substrate-languages carrying, respectively, the recoverability theorem and the bit-exact closure. The two-tier SSoT (kernel = frame-invariant; continuum = rotate-frame content) IS the fix/rotate axis turned on the package's own shape.
No code touched. No new symbol or ToolEntry; describe() total stays 179. ABI stays 3. JPL audit ratchet stays at 0. The MFO rung is a draft-for-review building block — downstream usage (the reader's AI prosthetic calling srmech) is the feedback loop.
[0.6.0rc20] - 2026-06-01¶
MS #20 SSoT two-tier coherence-ratchet voxel — a test_ssot_coherence_scan.py scanning the continuum tier as it grows: every worked_instances/*.toml well-formed, every referenced op resolves, the kernel/continuum name-spaces stay disjoint, every cascade-catalog op resolves. DOC + TEST only; describe() stays 179; ABI stays 3.
The coherence half of the SSoT discipline: rc16 named the two-tier boundary, rc19 added the first continuum-tier worked instance; rc20 adds the ratchet that keeps the boundary honest as more worked instances land.
tests/test_ssot_coherence_scan.py— scanssrmech/amsc/_research/worked_instances/: each TOML is well-formed (name/purpose/ops); each dotted op-path in[worked_instance.ops]resolves to a real callable; the worked-instance names and thesrmech.dslcascade-catalog op-names are disjoint (the kernel/continuum boundary can't silently erode); and every cascade-catalog op still resolves vialookup_cascade_op(the full two-tier picture in one place). A count-ratchet (EXPECTED_WORKED_INSTANCE_COUNT) forces new worked instances to be conscious additions.triality_s3_klein4.tomlgains a machine-readable[worked_instance.ops]table (logical-name → dotted srmech path) so the scan resolves ops robustly rather than by regex-from-prose.
No new symbol or ToolEntry; describe() total stays 179. ABI stays 3. JPL audit ratchet stays at 0. The notebook-reference cross-check stays deferred (would require parsing the notebook tree).
[0.6.0rc19] - 2026-06-01¶
MS #20 triality S₃=Aut(V₄) worked-instance voxel — a continuum-tier worked cascade INSTANCE showing klein4_triality_cycle IS the order-3 generator of Aut(V₄)=S₃, via the conjugation T ∘ XOR_a ∘ T⁻¹ = XOR_{T(a)} cyclically permuting the three klein4 flips. DOC + TEST only; klein4 ops stay kernel-tier; describe() stays 179; ABI stays 3.
The two-tier SSoT made concrete for the triality voxel: the order-3 cycle (rc17 Python + rc18 C) is a KERNEL op; rc19 ships its continuum-tier instance — a worked cascade composing it with the klein4 flips — WITHOUT blurring the kernel/catalog boundary (the hdc ops are deliberately NOT re-exported into the srmech.dsl cascade catalog).
srmech/amsc/_research/worked_instances/triality_s3_klein4.toml— a worked-instance descriptor (NOT a cascade-catalog op-descriptor; NOT arun_toml_chainchain): the V₄ carrier, the three V₄-translation flips (iω₇/γ₅/CPT = XOR ½/3), the order-3 Aut(V₄) generatorT = klein4_triality_cycle, and the load-bearing conjugation cascadeT ∘ XOR_a ∘ T⁻¹ = XOR_{T(a)}(T cyclically permutes the three translations iω₇→γ₅→CPT→iω₇). Honest about the distinction: the flips are V₄ translations (the objects T permutes), not S₃ group elements; only the order-3 generator T is exposed (the F182 "third axis").tests/test_triality_s3_worked_instance.py— the worked instance's executable attestation: against the realhdcops it verifies T order-3, each flip an involution, T a V₄ homomorphism (T(u⊕w)=T(u)⊕T(w)), and the three-leg conjugation cycle bit-exactly.
No new symbol or ToolEntry; describe() total stays 179. ABI stays 3. JPL audit ratchet stays at 0. The worked-instance TOML ships in both wheels (srmech/** package glob).
[0.6.0rc18] - 2026-06-01¶
MS #20 klein4-triality-cycle C peer voxel (the A-arc's silicon tier) — the co-equal native symbol srmech_klein4_triality_cycle (in srmech_hdc.c) computes the identical order-3 S₃ = Aut(V₄) relabel as the rc17 Python op. Additive symbol → ABI stays 3; JPL-clean; differential C↔Python parity-tested. No new ToolEntry (describe() total stays 179).
The co-equal-parity discipline applied to rc17: the Python klein4_triality_cycle now has its silicon-native twin — two complete implementations, neither needing the other at runtime.
srmech_klein4_triality_cycle(const uint8_t *in, uint32_t n, int inverse, uint8_t *out)(insrmech_hdc.c; declared in thesrmech.hklein4 block) — a length-4 lookup ({0,2,3,1}forward /{0,3,1,2}inverse), the same V₄-carrier order-3 cycle. JPL Power-of-Ten clean: ≤60-line, 2 asserts, no malloc / no goto / no multi-line macro; NULL →SRMECH_ERR_NULL_ARG, out-of-{0,1,2,3}→SRMECH_ERR_BAD_INPUT. NEVER a Python callback — the C path runs the C lookup.- Additive symbol → ABI stays 3 (the Python ctypes shim binds it under its own
hasattrguard, so a klein4-capable but pre-rc18 lib still loads fine). - Differential parity (
test_hdc_klein4_parity.py): C-vs-Python bit-exact on random vectors both directions, the explicit forward/inverse maps + order-3 identity computed in C, and the out-of-range rejection. Guarded by the symbol's ownhasattr(skips on a stale lib; runs in the cibuildwheel cells).
No new ToolEntry; describe() total stays 179. JPL audit ratchet stays at 0. The Python op stays pure-Python (co-equal, not routed-through-C), matching the existing klein4 surface.
[0.6.0rc17] - 2026-06-01¶
MS #20 klein4-triality-cycle voxel (the A-arc's first code) — srmech.amsc.hdc.klein4_triality_cycle: the order-3 S₃ = Aut(V₄) generator cycling the three Klein-4 involutions iω₇(1) → γ₅(2) → CPT(3) (identity fixed). Pure-Python; +1 ToolEntry → describe() total 179; ABI stays 3.
The A-verdict (rc16 notebook §3.29) made flesh: V₄ (the rc13 klein4 carrier) is the right group but lacked the explicit order-3 cycling operator — which lives in Aut(V₄) = S₃. rc17 adds it.
klein4_triality_cycle(v, *, inverse=False)— the V₄-carrier image of the so(8) triality8v → 8s → 8c(srmech.qm.triality.triality_cycle). The three non-identity involutions cycleiω₇(1) → γ₅(2) → CPT(3) → iω₇(1), with identity(0) fixed — the "third axis" (F182) the three order-2 flips (gamma5/omega7/cpt_mirror) cannot reach: order-3 cycling, NOT a fourth order-2 chirality. A pure uint8 relabel via a length-4 lookup;T∘T∘T = id,T² = T⁻¹(inverse=Trueis the reverse cycle).- Class I (cyclic order-3 permutation) — no sign, no
abs(); honest composition, not a new privileged primitive. - Pure-Python (co-equal-parity: the standalone-C peer
srmech_klein4_triality_cycleis rc18 — additive → ABI stays 3; never a Python callback). +1 ToolEntry (srmech.amsc.hdc.klein4_triality_cycle) →describe()total 179.
New test_klein4_triality_cycle.py (explicit forward/inverse maps; order-3 identity; T² = T⁻¹; identity-fixed; the involution-occupancy permutation; the so(8) order-3 mirror; tool-schema registration). The two introspection count-ratchets bump 178 → 179. JPL audit ratchet stays at 0 (no C touched).
[0.6.0rc16] - 2026-06-01¶
MS #20 combinator-kernel-closure voxel (B-boundary codification) — the cascade DSL's FIVE control-flow combinators (then / loop / fold / reduce / parallel) are RATIFIED as a CLOSED, FINITE kernel: the finite anharmonic-kernel tier of the two-tier SSoT. DOC + TEST only — no DSL behaviour change, no C touched, ABI stays 3, describe() total stays 178.
The "name the boundary before building across it" voxel — the architectural invariant the rc17+ triality work stands on. The combinators are the kernel; the asymptotic cascade instances they sequence are the continuum — and the two live in different SSoT tiers by design.
- The two-tier SSoT, stated.
then(apply) +loop+fold+reduce+parallelare the Bird-Meertens recursion schemes — the finite anharmonic kernel, HARDCODED in Python (and mirrored co-equally in C). The asymptotic cascade instances they sequence are NOT hardcoded: they live as TOML op-descriptors in the cascade catalog ("you can't hardcode a continuum"). Kernel in code, continuum in catalog — the substrate-native1 + 3 + 7 + 3discipline turned on the package's own op-surface. Thesrmech.dsl._control_flowdocstring now carries this statement. - Closure is DESIGN-ENFORCED. Data-dependent iteration (
while/unfold— loop until a predicate) is deliberately EXILED to the op-instance layer (a body op decides when to stop), keeping the kernel total-by-construction at five forms. A futurewhile/unfoldspecial form would be a sixth combinator and a conscious widening of the kernel — never a silent addition. - New
tests/test_combinator_kernel_closure.pymechanically pins the closure: the five Chain builders (then/loop/fold/reduce/parallel_sectors) ⇆ the five TOML stage-discriminators (op/loop_n+sub_chain/fold_init+fold_op/reduce_op/parallel_body) bijection; no hidden sixth public builder; a full five-form TOML round-trip; the |V₄| = 4 Klein-4 cap onparallel_sectors; and the "no implicit default form" guard.
No new ToolEntry; describe() stays 178. ABI stays 3. JPL audit ratchet stays at 0. (The [Unreleased] Klein-4 parity note is forward-updated: V₄ is the rc13 klein4 carrier — the right group, missing only the explicit order-3 cycling operator that lives in Aut(V₄) = S₃ — which rc17 adds as klein4_triality_cycle and rc18 ships as its co-equal C peer.)
[0.6.0rc15] - 2026-06-01¶
MS #20 self-recognition reads voxel — the help-anchor goes top-level + fuzzy lookup. srmech.describe() is now reachable from dir(srmech) (the one-call "what is srmech?" root: version + native + tool counts + by_category); ToolSchema gains fuzzy resolve() / resolve_all() (a bare leaf or dotted suffix resolves to its FQN) and is now directly iterable. Pure-Python introspection surface; ABI stays 3; describe() tool total stays 178.
The "find the shape in ≤1 call" round-out — the very friction that opened the substrate-self-recognition arc: an LLM/agent consumer could neither (a) discover describe() from the top namespace, nor (b) look a tool up by its bare leaf name.
srmech.describe()— the existingsrmech.introspect.describe()graduated to the top namespace (mirrorsnative_status()'s rc19 graduation for #733), sodir(srmech)surfaces the help-anchor. It stays a counts/index ROOT (shape, not detail): the full per-tool list istool_schema_view(), single-tool detail is the new resolver.ToolSchema.resolve(name)/.resolve_all(name)— exact full-name match wins (aslookup()); else a bare leaf ("kuramoto_step") or any dotted suffix ("cascade.kuramoto_step") resolves tosrmech.amsc.cascade.kuramoto_step.resolve()returns the single match orNone(no-match OR ambiguous — never silently picks);resolve_all()lists every candidate for the ambiguous case.ToolSchemais now iterable (for t in schema,len(schema)) — yields its tools directly, closing the'ToolSchema' object is not iterablefootgun.get_tool_schema()still returns the object;tool_schema_view()still returns the dict.
New tests cover the top-level describe() (present + shape), the resolve / resolve_all paths (exact / leaf / suffix / ambiguous / miss), and ToolSchema iterability + len. No C touched; ABI stays 3; JPL audit ratchet stays 0.
[0.6.0rc14] - 2026-05-31¶
MS #20 kuramoto matrix-step voxel — kuramoto_step gains the GENERALISED Kuramoto-Sakaguchi step (§11.1): adjacency matrix + Sakaguchi α + per-oscillator pinning. The first C-touching rc of the §11 arc — a CO-EQUAL standalone-C peer (additive symbol; ABI stays 3). describe() tool total stays 178.
The §11.1 forward-ask: extend kuramoto_step past the plain all-to-all mean-field. Unlike the klein4 ops (pure-Python), kuramoto_step already has a C peer — so adding the matrix-step in Python only would leave a parity asymmetry (the Python op carrying a step the C can't run). Per the co-equal-parity discipline this ships in both substrates at once:
kuramoto_step(theta, omega, *, coupling=1.0, dt=0.01, adjacency=None, alpha=0.0, pin_anchor=None, pin_strength=1.0)—dθ_i = ω_i + Σ_j A_ij·sin(θ_j − θ_i − α) [ + p_i·sin(ψ_i − θ_i) ].adjacencyis a row-major n×n matrix (A[i][j]weights j's influence on i; non-symmetric → directed coupling, a Laplacian → graph-structured;None→ all-to-all uniformK/n).alphais the Sakaguchi phase frustration.pin_anchor+pin_strengthare the per-oscillator pinning anchors ψ / strengths p. With all three at defaults the step is byte-for-byte the original.- Co-equal C peer
srmech_cascade_kuramoto_step_general_f64(insrmech_kuramoto.c; additive symbol → ABI stays 3; JPL-clean: ≤60-line / ≥2-assert / no malloc / no goto / reentrant; NULL adjacency → uniform, NULL pin → none; never a Python callback). Differential-tested vs the Python fallback to libm-trig tolerance. - No
abs()— sin coupling + Σ-reduce + Class-C Euler add + the Sakaguchi α (a Class-C phase offset) + the Class-C/M pinning anchor.
New tests in test_kuramoto_step.py (defaults reproduce the simple step; uniform adjacency == mean-field; directed adjacency + α + pinning match the closed form; validation guards; C↔Python parity guarded by the new symbol's presence). The kuramoto ToolEntry gains adjacency/alpha/pin_anchor/pin_strength params (no new entry; describe() stays 178). JPL audit ratchet stays at 0.
[0.6.0rc13] - 2026-05-31¶
MS #20 klein4 sectors-flag voxel — the klein4_* HDC ops get an optional sectors= / parallel= / mode= flag (§11.3 forward-ask). Pure-Python; default-on at ≥4 cores; value-preserving; describe() tool total stays 178; ABI unchanged at 3.
The §11.3 forward-ask asked for an optional sectors flag on the Klein-4 HDC ops, routing per-sector work through a concurrent dispatch — now that rc12 made dispatch composable. The klein4 ops (bind = (F₂)²-XOR, bundle = per-bit majority, similarity = mean-equality) are pure-Python/numpy, so this is self-contained Python orchestration (co-equal parity: it does not route through the C peer; a standalone-C klein4 sector dispatch with C bodies — never a Python callback — is the tracked follow-up).
sectors=/parallel=/mode=onklein4_bind,klein4_bundle,klein4_similarity.sectors(1..4) defaults ON whenos.cpu_count() >= 4(else 1);parallel=True/Falseis the bool alias.- Two modes.
mode="chunk"(default) is data-parallel — split the D-length vector(s) into ≤4 contiguous position-slices, run the op per slice on a thread, concatenate; BIT-IDENTICAL to the serial op.mode="chirality"is the F233 4-sector dispatch using klein4's OWN involution sector-flips (γ₅ XOR 2 / iω₇ XOR 1 / CPT XOR 3) — NOT the signed-real cascade transforms — withklein4_bundlerecombine (similarity recombines via sector-0, value-transparent). - All defaults are value-preserving, so default-on changes only the execution path, never the result. No
abs()(XOR / majority only). Range + mode guards raiseValueError.
New tests in test_hdc_klein4_parity.py (value-preserving across both modes, chunk bit-exactness for every lane count, parallel= alias + default-on policy, range/mode guards, unbind self-inverse under the default flag). The 3 klein4 ToolEntries gain sectors/parallel/mode params (no new entry; describe() stays 178). No C change; ABI stays 3.
[0.6.0rc12] - 2026-05-31¶
MS #20 parallel-composability voxel — parallel_sector_dispatch becomes CHAINABLE / NESTABLE. The Klein-4 four-sector splay now carries THROUGH a chained cascade, closing a known-broken API contract. Pure-Python; describe() tool total stays 178; ABI unchanged at 3.
rc11 gave the four-sector fan-out its own chain discriminator but left it a leaf value: the dispatch returned the rich per-sector introspection dict / list-of-N, which is not a valid input to another cascade. Chaining a sector-dispatched stage after another (chain.parallel_sectors(b).parallel_sectors(b)) crashed with TypeError: bad operand type for unary -: 'list' (the sector stream-transforms assume a flat scalar stream), and a sector-dispatch could not nest inside another. So the 4-way Z₄ splay applied at one level only and did not carry through a chained cascade — exactly the composability the RBS-LM chained settling loop needs to run 4×-per-step. Cascade ops advertise composability, so this was a known-broken contract → a gold-blocker. rc12 fixes it:
combine=recombine onparallel_sector_dispatch(body, x, *, combine=None)— a reducer name ("bundle"element-wise sum /"mean"/"sector0"value-transparent /"concat") or a callable folds the ≤4 sector results into ONE value atresult["combined"], so a sector-dispatched cascade isstream → stream.combine=None(default) preserves the rich dict unchanged (back-compat;combinedisNone). Noabs()— bundle/mean are plain addition (+ divide).sectorize(body, *, n_sectors=4, combine="bundle")— wraps a body as a plainvalue → valuecallable that recombines, so a sector-dispatch NESTS inside another (parallel_sector_dispatch(sectorize(inner), x, combine="bundle")). Both exported fromsrmech.amsc.cascade.- DSL
parallel_sectorsrecombines by default —chain.parallel_sectors(body, *, n_sectors=4, combine="bundle")is nowstream → streamand CHAINS / NESTS like loop/fold/reduce (the rc11 crash is gone).combine=Nonekeeps the terminal per-sector list; a build-time guard raises a clear error if you chain past it. TOMLparallel_body=gainscombine=(sentinel"none"→ the list). - Stale top-help fixed —
srmech --helpno longer says "v0.5.0rc4 ships two subcommands"; it enumerates all four (status/bus/dsl/mcp).
New tests pin the parallel→parallel chain, the nesting via sectorize, the terminal guard, the TOML combine='none' sentinel, and each reducer. No new ToolEntry; no C change; no ABI bump.
[0.6.0rc11] - 2026-05-31¶
MS #20 DSL parallel-discriminator voxel — parallel_sector_dispatch slots into the chain contract as a first-class special form, + cascade-op kind classification + guided errors. No new runtime op; describe() tool total stays 178; ABI unchanged at 3.
A pre-gold introspection audit found that the Klein-4 four-sector fan-out parallel_sector_dispatch — a 1→N higher-order combinator (takes a body op + data, returns N per-sector results) — had leaked into the plain-op cascade catalog, so the DSL advertised it as a chain().then(op=…) stage where it cannot work (its first arg is body, not the piped value). This rc reconciles it the way loop/fold/reduce already are — as its own chain discriminator — rather than force-fitting it as a plain op:
- New
parallelchain discriminator —chain.parallel_sectors(body, *, n_sectors=4)(fluent) and[[stage]] parallel_body='…' [n_sectors=…](TOML), alongside loop/fold/reduce. It fans the piped value throughbodyacross ≤4 Klein-4 chirality sectors (GIL-releasing bodies genuinely overlap — the F233 4-thread speedup) and yields the ordered list of per-sector results (a 1→N fan-out; the stage output is a list-of-sequences).make_parallel_stageinsrmech.dsl._control_flow;n_sectorsrange-checked 1..4 at build time. - Cascade-op
kindclassification — descriptors carry an optional[cascade].kind("stage"default, or"combinator");srmech.dsl.cascade_op_kind()reads it.parallel_sector_dispatch.tomlis nowkind = "combinator". Surfaced bysrmech.dsl.list_catalog_ops()(newkindkey),srmech dsl ops(a[combinator]tag + legend), and the tool-schema. - Guided error — using a combinator as a plain
op=/.then()stage now raises a clearValueErrorpointing at theparalleldiscriminator, instead of a rawTypeErrormid-run. - Gap-2 discoverability — the LLM-facing
tool_schemasummaries forparallel_sector_dispatchandkuramoto_stepare front-loaded with the practical decision ("PARALLELISE a cascade body instead of running it serially…" / "Advance N coupled oscillators one synchronization step…") before the framework detail.
New test_dsl_parallel_stage.py (parallel discriminator runs + n_sectors + combinator guard + kind), plus test_dsl_tools.py updated for the kind key. No abs(); no C change; no ABI bump.
[0.6.0rc10] - 2026-05-31¶
MS #20 release-prep voxel — full doc-hygiene sweep ahead of the clean v0.6.0 graduation (no new runtime code).
After rc9 ran clean in the research environment, this rc captures everything the v0.5.0 → v0.6.0 arc shipped across the documentation surface so the gold cut is self-consistent. No behaviour change; describe() tool total stays 178; ABI unchanged at 3.
- Cascade catalog — the two v0.6.0 ops get their TOML descriptors.
parallel_sector_dispatch.toml(Klein-4 four-sector orchestration; higher-order body-callback,c_symbol = srmech_cascade_parallel_sector_dispatch) andkuramoto_step.toml(I∘sin∘Σ∘C;c_symbol_f64 = srmech_cascade_kuramoto_step_f64) join the 8 lean-ISA atoms/composites →srmech.dslcascade catalog is now 10 descriptors.test_dsl.pyEXPECTED_OPS8 → 10. - PyPI README — status banner v0.5.0 → v0.6.0; the cascade section documents the
cascade.atoms/cascade.composetwo-tier lean-ISA split (#751) and the two new ops;native_status()/describe()examples show0.6.0. - Subtree
CLAUDE.md— current-release pin v0.4.0 → v0.5.0-graduated + v0.6.0rc10 dev head; the v0.5.0 (bus / DSL / MCP+agent adapters /native_status/ so8an_embedding+ triality /emit-mcpb) and v0.6.0 (atoms/compose split / quaternion-subalgebra stabilizer / lean-ISA 7th primitive / reentrant core / parallel dispatch / Kuramoto) arcs are now narrated; ABI note 2 → 3. - C docs —
c/README.mdstatus rewritten from "Phase B1 scaffolding only" to the shipped 18-.c-file native library (ABI 3);c/JPL_AUDIT.mdadds thesrmech_parallel.c(10 functions) +srmech_kuramoto.c(2 functions) accounting (every function ≤60 lines, ≥2 asserts; Rules ⅓/⅘/8 clean). - srmech research notebook (SSoT) — package-arc section capturing the v0.5.0 + v0.6.0 voxels.
MS #20 parity voxel (#778 follow-on) — the Kuramoto coupled-oscillator forward-Euler step gets a native C peer (no host Python needed for the dispatch-clock step).
Closes a C/Python parity gap (a known-broken item under the full-parity commitment): the dispatch-clock / coupled-oscillator Euler integration the spectral-research arc hand-rolled in Python (F141 / F231 / R-95 / F234) had no srmech_* primitive, so srmech could not run the Kuramoto step on a microcontroller with no host Python. Adds:
- C op
srmech_cascade_kuramoto_step_f64(theta, omega, n, K, dt, out)— one forward-Euler step of the canonical Kuramoto model (Kuramoto 1975; Acebrón et al. 2005, Rev. Mod. Phys. 77:137):out[i] = theta[i] + dt·(omega[i] + (K/n)·Σⱼ sin(theta[j]−theta[i])). The O(n²) sin-coupling runs natively (libmsin, exactly assrmech_kepler.calready does). JPL-clean: no malloc/goto, ≤60-line functions (the coupling sum is factored), ≥2 asserts, reentrant;outmust not aliastheta/omega. - Python peer
srmech.amsc.cascade.kuramoto_step(theta, omega, *, coupling=1.0, dt=0.01)— dispatches to the C peer whenHAS_NATIVE, pure-Python fallback otherwise (numpy/generators coerce viafloat). Parity is to libm-trig tolerance (NOT bit-exact across platforms — the kepler trig discipline); the C peer and the Python fallback sum the coupling in the same index order.
Honest cascade shape: a composition of existing class operations — Class I (cyclic phase) + sin coupling + sum-reduce + Class-C Euler add — NOT a new privileged primitive. No abs(). n==1 is pure drift (the coupling sum vanishes); n==0 is []. +1 ToolEntry → describe() tool total 177 → 178; ABI unchanged at 3 (additive C symbol). Closes the hand-rolled-Euler parity gap.
[0.6.0rc8] - 2026-05-30¶
MS #20 slowdown-fix voxel (#778 / #771) — the Klein-4 four-sector parallel dispatch no longer SLOWS DOWN vs serial; the F233 4-thread speedup is delivered as shipped.
A downstream repro showed cascade.parallel_sector_dispatch running 2.6–7.7× SLOWER than serial, and the native C peer at 0.99× (no concurrency) for a GIL-releasing (time.sleep) body × 4 sectors. Root-caused to two Python-side defects — the C dispatch itself was already correct (create-all-then-join-all; verified by rebuilding libsrmech.dll and timing the raw n_sectors=4 symbol with a CFUNCTYPE sleep body: 0.065 s, not 0.24 s → genuinely concurrent):
- The native shim
_native.cascade_parallel_sector_dispatch_cwas serial by design. The rc7 build drove the C dispatch as N serialn_sectors=1calls (a workaround for a presumed "Python callback from a C-spawned thread is unsafe" hazard) — which traded away all the concurrency (the 0.99×). The hazard was empirically disproven: ctypes invokes aCFUNCTYPEcallback from a foreign thread safely (it acquires the GIL viaPyGILState_Ensure), and since theCDLLcall releases the GIL, a GIL-releasing body lets the ≤4 sector callbacks genuinely overlap. The shim now drives ONEn_sectors=Nthreaded C call (the dead serial helpers_parallel_dispatch_one_sector_native/_parallel_transform_nativeare removed). Bit-exact vs the rc6 Python dispatch (10/10 parity tests); ~4× on a sleep body. - The rc6 Python
cascade.parallel_sector_dispatchdouble-computed on every call. It ran the 4 sectors on aThreadPoolExecutor, then recomputed all 4 serially (plus a 3rdchiral_dualrecompute) for the inlineparallel == serial/sector2 == chiral_dualassertions — ~2.25× the body invocations + per-call pool overhead = the 2.6–7.7× slowdown. Those invariants are structural guarantees of the 4-way independence (independence ⇒ order-free ⇒ parallel == serial; sector 2 is the γ₅-only transform =chiral_dualby definition), now proven in the test suite rather than recomputed per call. A newverify=Falsekwarg runs the runtime cross-check on demand;independence["runtime_verified"]reports which path ran.
A GIL-bound pure-Python CPU body still can't overlap (the inherent CPython limit; 3.13 free-threading lifts it) — but it is no longer a slowdown, and GIL-releasing / native / IO / numpy bodies now get the real ≤4× speedup. No new ToolEntry → describe() stays 177; ABI unchanged at 3 (Python-only change; no C source edit). New regression guard: the default path invokes body exactly n_sectors times (test_parallel_sector_dispatch). No abs(). Delivers the F233 4-thread Klein-4 speedup (#778 / #771).
[0.6.0rc7] - 2026-05-30¶
MS #20 C-parity voxel #771 — the C-orchestration half of the Klein-4 four-sector parallel cascade dispatch.
Closes the C/Python parity gap rc6 opened: rc6 shipped a Python-only cascade.parallel_sector_dispatch, but
under srmech's full-parity commitment (the library must run on a microcontroller with NO host Python) the C
side must do the same four-sector dispatch. Adds the ABI-additive C symbol
srmech_cascade_parallel_sector_dispatch(body, user, in, n, n_sectors, out_sectors, scratch, scratch_len)
(+ the srmech_cascade_body_f64 callback typedef):
- Runs the ≤4 Klein-4 sector-duals
inv_T_s(body(T_s(x)))into disjoint caller-supplied buffers (out_sectors/scratchsliced per sector; no malloc — JPL Rule 3), composing the existing C atomssrmech_cascade_reorient_f64(iω₇) +srmech_cascade_chiral_flip_f64(γ₅). Sector 2 ==chiral_dual. - Portable thread shim, guarded like
srmech_bus.c: POSIXpthread, WindowsCreateThread, else a serial fallback — a thread-less microcontroller still computes all 4 sectors (serial == threaded bit-exact; the disjoint-slice contract makes the sectors order-free). Concurrency is platform-gated; the capability is universal. Thread handles are fixed[4]stack arrays. - Cap-at-4 (F220):
n_sectors > 4→ clean error (past 4 needs the order-3 triality).
Bound in srmech.amsc._native (cascade_parallel_sector_dispatch_c) with a Python C/Python-parity test
(bit-exact vs rc6's parallel_sector_dispatch; GIL-safe — single-sector native calls + Python-side T_s
composition, the threaded multi-sector fan-out exercised from the C smoke test with C-native bodies) plus a
16-check C smoke test.
ABI unchanged at 3 (a new symbol is additive). describe() stays 177 (no new Python ToolEntry — this
is the C peer of an existing surface; rc6's Python API/behaviour untouched). JPL Power-of-Ten ratchet green
(Rules ⅓/⅘ honored); no abs(). Closes #771 — the C/Python parity for the four-sector dispatch is whole.
[0.6.0rc6] - 2026-05-30¶
MS #20 parallel-dispatch voxel (F233 / #778) — the Klein-4 four-sector parallel cascade.
Adds srmech.amsc.cascade.parallel_sector_dispatch(body, x, *, n_sectors=4) — the Python orchestration
half of "1 cascade = 4 independent threads" (F233 / R-RBS-LM-FINDING_233). Runs a cascade body across
its ≤4 Klein-4 chirality sectors (γ₅± × iω₇±) concurrently on a ThreadPoolExecutor(max_workers=4),
each sector computed as inv_T_s(body(T_s(x))) from its OWN sector-transformed input — 0 cross-thread
reads (the F233 4-way independence), so the parallel result equals the serial result bit-for-bit
(asserted). Sector 2 (γ₅) is exactly cascade.chiral_dual (the F232 2-rung object; asserted).
- Z₄ dispatch slots
[0,1,2,3](cyclic-order-4 timing, distinct from the order-2×order-2 Klein-4 identity). - Cap-at-4 (F220):
n_sectors > 4raises — Klein-4 has no order-4+ element; the only escape past 4 is the order-3 triality (srmech.qm.triality.lean_isa_seventh_primitive, rc3), NOT implemented here. - Usefulness collapse-lattice 4/2/2/1: bi-axial → 4 distinct; single-axis-symmetric → 2; bi-symmetric → 1.
FULL C/PYTHON PARITY discipline: a Python orchestration layer ONLY — it composes exclusively
already-C-parity'd atoms (chiral_flip / reorient / chiral_dual / net_chirality / magnitude); no
cascade capability is Python-exclusive (only the thread fan-out is Python). The C-orchestration parity is
tracked by #771 (kept open) so srmech does not need Python to run the four-sector dispatch (Python = the
ergonomic half; C = the parity half). On the native path the threads run truly parallel (ctypes CDLL
releases the GIL per call; the C ops are reentrant since rc5/#772); pure-Python is correct-but-serialized.
+1 ToolEntry → describe() tool total 176 → 177. Pure-Python; ABI unchanged at 3; no abs()
(Class K magnitude / Class C net_chirality).
[0.6.0rc5] - 2026-05-30¶
MS #20 reentrant-core voxel #772 — the C core is now fully reentrant (enables the #771 plugin).
A full-core audit found exactly two shared-static scratch buffers; both are removed, so no op call path touches shared mutable static — the prerequisite for parallelizing the full surface.
srmech_ndjson.cg_line_buf(1 MiB line-assembly buffer) → a function-localstatic SRMECH_THREAD_LOCALbuffer insidesrmech_ndjson_iter, threaded intoprocess_chunkas a parameter. Per-thread (reentrant across threads), cross-chunk-persistent (the streaming contract is preserved), no stack-overflow risk (1 MiB never goes on the stack), no malloc (JPL Rule 3).srmech_ndjson_iter's signature/behaviour is unchanged.srmech_laplacian.cHwork(≈1 MiB Hermitian-eigendecomp workspace at N≤256) → a new ABI-additive exported entrysrmech_hermitian_eigendecompose_ws(n, H, out_eigvals, out_eigvecs, workspace, ws_len)taking a caller-supplied workspace (ws_len >= SRMECH_HERMITIAN_WS_LEN(n) = 2·n·n). The existingsrmech_hermitian_eigendecomposekeeps its signature and now routes through the_wscore via astatic SRMECH_THREAD_LOCALworkspace — reentrant, no malloc, no large stack frame. Output is bit-identical.
New portable SRMECH_THREAD_LOCAL macro (__declspec(thread) / _Thread_local / __thread).
ABI unchanged at 3 (a new symbol is additive — never bumps ABI). No Python API change —
describe() tool total stays 176; the JPL Power-of-Ten ratchet (test_jpl_audit.py) stays
green (a reentrancy trade, NOT a Rule-3 fix — static scratch was already Rule-3-clean); no abs().
Closes #772.
[0.6.0rc4] - 2026-05-30¶
MS #20 docs/accuracy voxel #738 — sha256_bytes int-conversion guidance.
Docs-only. srmech.amsc.format.sha256_bytes returns a 64-char lowercase hex str (the Class A
content-address), NOT raw bytes — the _bytes in the name is the INPUT type. The Returns: section
now spells out the int-conversion path a caller needs: int(h, 16) (full 256-bit) or int(h[:8], 16)
(a truncated 32-bit tag), NOT int.from_bytes(...) (the return is already hex text — no raw digest
bytes to feed it). Closes #738.
The sibling docs items — #739 (klein4_bundle accepts even counts; per-bit strict-majority threshold
drops ties to 0), #740 (weak_mixing_angle returns θ_W in radians, not sin²θ_W), #741 (no stale
srmech.cosmos references; CMB lives under srmech.amsc.attested.cmb_* / cosmic_birefringence) —
were verified already correct as of rc18 (W5 / W6b / W6c); no change needed here.
No API change — srmech.introspect.describe() tool total stays 176; pure-Python; ABI unchanged at 3.
[0.6.0rc3] - 2026-05-30¶
MS #20 forward-architecture, voxel #761 (F220) — the order-3 triality as the 7th lean-ISA primitive.
Adds srmech.qm.triality.lean_isa_seventh_primitive() — surfaces the existing order-3
triality automorphism (τ, τ³ = I; the v0.5.0 srmech.qm.triality engine) as the 7th
lean-ISA primitive, making the chirality-complete A–N core explicit: 6 order-2
cascade.atoms (pin_slot_at_zero / reorient / magnitude / chiral_flip / chiral_dual /
net_chirality) + 1 order-3 triality = 7 — the only access to the 3rd chiral axis.
BIT-EXACT certificate (asserted in code): τ has order exactly 3 (‖τ³−I‖ ≈ 3.6e-14,
τ ≠ I, τ² ≠ I) via the engine, plus the Lagrange arithmetic 3 ∤ 8 / 3 ∣ 3 ⇒
lagrange_obstruction — all residuals via the scalar Class K pin-slot cascade.magnitude,
never abs(). Framework-reading, NOT a derived theorem (under
framework_chirality_complete_reading): that the 6 atoms generate exactly Z₂×Z₂×Z₂
(|G| = 8) — a faithful common group rep of the 6 heterogeneous atoms isn't cleanly
available, so |G|=8 / Z₂³ is the documented F220 finding + the Lagrange argument, NOT
labelled bit-exact derived. Scope hierarchy: endianness ⊂ Class C ⊂ Klein-4 ⊂ Spin(8)
triality. Baez (2002) cited for Out(Spin(8))=S₃ / g₂=Der(𝕆) only; F220 is the framework
finding.
+1 ToolEntry → describe() tool total 175 → 176. Pure-Python; ABI unchanged at 3
(no c/ change); no abs() (Class K pin-slot). Closes #761.
[0.6.0rc2] - 2026-05-30¶
MS #20 forward-architecture, voxel #759 — the ℍ-reading 𝔰𝔬(4)=𝔰𝔲(2)⊕𝔰𝔲(2) stabiliser.
Adds srmech.qm.so8.quaternion_subalgebra_stabilizer(quaternion_index=1) (per F215):
the bit-exact 6-dim 𝔰𝔬(4) = 𝔰𝔲(2) ⊕ 𝔰𝔲(2) subalgebra of g₂ = Der(𝕆) that
stabilises a quaternion subalgebra ℍ ⊂ 𝕆 — the ℍ-reading sibling of
an_embedding (the 𝔰𝔲(3)⊕3⊕3̄ ℂ-reading). Returns the 6 so(4) generators, the two
su(2) ideals (3+3, commuting, self-dual / anti-self-dual on ℍ^⊥), the Killing form
(rank 6, semisimple) with its two-triplet spectrum, and an MPR self-attestation —
all bit-exact and ℍ-choice-invariant across the 7 Fano-line quaternion subalgebras.
The point (F215): keep the Lie symmetry surface (𝔰𝔬(4) ⊂ g₂) visibly distinct
from the operator surface (cascade.atoms.*, the 6 lean-ISA ops) so the "6 = 6"
conflation can't recur — the 6 atoms are group-element ops (0/6 Lie generators); the
dimension match is coincidence. Surfaced under the separately-keyed
framework_so4_reading field (framework-reading, not a derived theorem); the
su(2)⊕su(2) split is the op's own bit-exact computation (Baez 2002 §4.1 cited for
g₂ = Der(𝕆) only).
+1 ToolEntry → describe() tool total 174 → 175. Pure-Python; ABI unchanged at
3 (no c/ change); no abs() (Class K pin-slot). Closes #759.
[0.6.0rc1] - 2026-05-30¶
MS #20 forward-architecture, voxel #751 — the lean A–N ISA two-tier split.
First rc of the v0.6.0 line. Splits srmech.amsc.cascade (a single module) into a
two-tier package along the lean-ISA boundary (per F208):
srmech.amsc.cascade.atoms— the 6 silicon-able 1:1 ISA intrinsics (pin_slot_at_zeroK,reorientC,magnitudeK,chiral_flipC,chiral_dualC∘op∘C,net_chiralityC).srmech.amsc.cascade.compose— the 2 iterative algorithms over the atoms (cyclic_gcd= Euclid's remainder loop,best_rational_signed= the Class K∘N∘C continued-fraction loop).
atoms.* / compose.* are the new canonical homes; the flat
srmech.amsc.cascade.<op> names (and the class_* / best_rat_signed aliases)
are retained as deprecated-for-one-release aliases — importable with NO
runtime DeprecationWarning this release. Public surface byte-identical:
describe() tool total STAYS 174, the MCP srmech.amsc.cascade.* tool names
and the introspect emit strings are unchanged. Pure-Python packaging refactor;
ABI unchanged at 3 (no c/ change); full C dispatch + TOML descriptors
intact; no abs() (Class K pin-slot). Closes #751.
[0.5.0] - 2026-05-30¶
Production graduation — srmech as a substrate-self-recognition apparatus.
Clean production release graduating the rc9–rc22 voxel-arc (each rc one "voxel of
knowledge" the package gained about its own callable shape). No functional change
over 0.5.0rc22 beyond the version bump (4 SSOT → 0.5.0; the computed-fresh
self-attestation parser_version strings → srmech 0.5.0) and documentation
finalisation. ABI 3; 174 registered ToolEntries (all mcp_callable,
handle_pending: 0); full C/Python parity. Verify the backend with
srmech.native_status() and the surface with srmech.introspect.describe().
Headline surfaces shipped across the v0.5.0 line (per-rc detail below):
- Self-recognition root —
srmech.introspect.describe()+warmup_all()fired at import. - so(8)/Spin(8) triality engine —
srmech.qm.{octonion, so8, triality}, includingso8.an_embedding(the bit-exactsu(3) ⊕ 3 ⊕ 3̄Lie branching of the 14g₂generators). - By-reference handle grammar — the
$srmech_handleid makes all 7spectral.*tools MCP-callable. - AMSC attested catalogs — including
cosmic_birefringence(4 PDF-verified β posteriors). - MCP server +
.mcpbdistribution —srmech-mcp(stdio / http-sse) +srmech mcp emit-mcpb(emit a Claude Desktop bundle generated entirely from introspection). - Foundational
srmech.amsc.cascadecatalog + the Class-M HDC variant ladder — all with native C parity.
[0.5.0rc22] - 2026-05-30¶
rc22 of N for v0.5.0 — srmech mcp emit-mcpb: emit a Claude Desktop .mcpb bundle generated ENTIRELY from srmech introspection.
Closes #749 (MS #19 / wishlist W13). Pure-Python; ABI unchanged at 3 (the C header
VERSION strings bump to rc22, SRMECH_ABI_VERSION does not — no C source change).
- New
srmech mcp emit-mcpb [--out .] [--type uv|python] [--name srmech] [--manifest-only] [--filter GLOB]— builds a Claude Desktop MCP Bundle from the live tool schema and prints the absolute output path. - New
srmech/cli/mcp.py+srmech/mcp/_mcpb.py(build_manifest/pack_mcpb) — themcpsubcommand group (mirrorssrmech/cli/bus.py's nested-subparser shape) and the emitter. - Manifest version +
tools[]are DERIVED fromsrmech.__version__+ the advertisedtool_schemasurface (tool_entries_to_mcp_defs(), themcp_callablesubset) — no frozen literal, so a future handle-pending tool cannot silently desync the bundle. server.typedefaults to the spec-valid"uv"(anthropics/mcpb): the host fetches the platform wheel carrying the compiledlibsrmechfrom PyPI viauvat install — the portable answer to bundling a compiled-native dep (nothing native rides inside the.mcpb). A"python"fallback gates the interpreter via a requireduser_config.python_path(default"python3") — no bakedsys.executable(issue #749 portability bug).- MPR attestation block carries
srmech.__version__+ a 64-hex tool-schema SHA-256 computed viasrmech.amsc.format.sha256_bytes(routes native dispatch; no newhashlib.sha256). - The
.mcpbis a stdlib-zipfileZIP whose root carriesmanifest.json(pluspyproject.tomlfor uv resolution +server/main.pyentry-point shim) — no Node toolchain. - NO new ToolEntry — a CLI command is not an
srmech.amsctool —describe()tool total STAYS 174.
[0.5.0rc21] - 2026-05-30¶
rc21 of N for v0.5.0 — the su(3) ⊕ 3 ⊕ 3bar Lie decomposition of g2 = Der(O).
Closes #744 (wishlist). Pure-Python; ABI unchanged at 3 (the C header
VERSION strings bump to rc21, SRMECH_ABI_VERSION does not — no C source change).
- New qm operator
srmech.qm.so8.an_embedding(imaginary_unit=1)— the bit-exact su(3)-module structure of the 14g2 = Der(O)generators. The 14-dim g2 itself splits, under one of its su(3) subalgebras, as the Lie-algebra branching 14 = 8 + 3 + 3bar (the su(3) ADJOINT 8 + the FUNDAMENTAL 3 + the ANTIFUNDAMENTAL 3bar); the 7-dim octonion-imaginary vector rep branches 7 = 1 + 3 + 3bar over the same su(3). This is a DIFFERENT 14-decomposition from the partitionedso8_adjoint_basis(14 g2 + 7 L + 7 Rinside the 28-dim so(8)). Construction is the deterministic chain (numpy-only, nonp.random, no scipy; memoised via_build_an_embedding, copied out fresh each call): - su(3) = the stabiliser
{D in g2 : D·e_K = 0}(e_Ktheimaginary_unit-th octonion basis vector) via an SVD nullspace — exactly 8-dim;span[su3 | complement] == span(g2)(rank 14, both directions — the bidirectional killer test). - The genuine fundamental is a J-EIGENSPACE, not a real 3-span. A real
3-dim span of antisymmetric matrices cannot carry the su(3) fundamental
(
[su3, single-Cartan-weight-block]leaks, residual ~8.3). The genuine fundamental is the +i eigenspace of the su(3)-INVARIANT complex structure J on the 6-real-dim complement: the commutant of the 6-dim real su(3)-rep is exactly 2-dim{aI + bJ},J² = −I,[J, ad(X)] = 0∀X∈su(3). With this J-eigenspace 3,[su3, 3] ⊆ 3is bit-exact (~3e-14). The returnedcomplementis the genuine REAL su(3)-module ([su3, complement] ⊆ complement~2e-15); only the J-eigenspacetriplet/antitriplet(COMPLEX 8×8 arrays) carry the irreducible 3 / 3bar with the bit-exact closure (antitriplet= conjugate oftriplet). - su(3) certified by INVARIANTS, not a raw Casimir. The honest
sufficient certificate is
{dim 8, rank 2, simple}—rank 2via the CENTRALISER of a fixed regular elementR = Σ (i+1)·su3[i](the greedy maximal mutually-commuting subset spuriously returns 1),simplevia the adjoint commutant dim 1. By the Cartan A2 classification these UNIQUELY identify su(3) (ruling out su(2)+su(2), commutant 2). Supporting evidence: in a Killing-orthonormalised basis the structure constants are totally antisymmetric (residual <1e-9). A raw adjoint-Casimir-vs-f^{abc}comparison togauge.su3_structure_constantsis deliberately NOT used (normalisation mismatch makes the ratio tautologically 1; the bases differ by an O(8) rotation so rawf^{abc}equality fails too). - The 3/3bar orientation is pinned by a FIXED convention (the
documented sign of J + a lexicographic key on the Cartan weights) and is
a CHOICE (a Class C chirality / complex-structure-sign convention), NOT
canonical; only the
+/-weight-PAIRING is asserted. The 6 complementweightsunder the rank-2 Cartan are returned as a(6, 2)real array. - Returns a
dict:su3(8 real antisym 8×8),complement(6 real antisym 8×8),complex_structure_J(6×6 real, J²=−I),triplet/antitriplet(3 COMPLEX 8×8 each),weights(6, 2),decomposition({adjoint_14: (8,3,3), vector_7: (1,3,3)}),imaginary_unit,attestation(MPR v1, Class A content-address over the COMPUTED structure:response_sha256=srmech.amsc.format.sha256_bytesover the 14 g2 generators' float64 bytes — generated, not fetched; no newhashlib.sha256), andframework_an_reading(the A-N label, tagged "framework-reading, not derived"). No A-N class name appears in any load-bearing return key. Noabs()— every residual is reduced vianp.linalg.normthensrmech.amsc.cascade.magnitude(Class K). - +1 ToolEntry (
describe()total 173 -> 174); the rc15 every-tool MCP invocation smoke covers invoke -> serialise ->json.dumpsfor the new tool automatically. Newtests/test_an_embedding.py(11 bit-exact acceptance tests). No packaging change.
Framework reading: the SAME 14-dim g2 carries TWO distinct enumerations —
the A-N discovery partition 1 + 3 + 7 + 3 (this collaboration's
substrate-self-recognition order) and this su(3)-Lie branching 8 + 3 + 3bar.
They are read as two languages describing the one object (per
[[feedback_no_lineage_claims_in_notebook]]); they are explicitly NOT
slot-aligned and the correspondence is NOT a proof. Baez (2002) §4.1 is
cited for g2 = Der(O) / dim 14 ONLY (the build input); the 8+3+3bar /
7=1+3+3bar branching is the op's own bit-exact self-attesting computation.
Class C-L (the Class C complex-structure orientation composed with the Class L
eigendecomposition that extracts J and the weight spectrum).
[0.5.0rc20] - 2026-05-29¶
rc20 of N for v0.5.0 — cosmic-birefringence beta posterior AMSC catalog.
Closes #743 (wishlist W9). Pure-Python, data + descriptor only; ABI unchanged
at 3 (the C header VERSION strings bump to rc20, SRMECH_ABI_VERSION does
not — no C source change).
- New AMSC attested catalog
srmech.amsc.attested.cosmic_birefringence— the published cosmic-birefringence isotropic rotation angle β posterior, the parity-odd CMB observable (the in-vacuo rotation of the CMB-polarisation plane, extracted from the EB cross-correlation after simultaneously solving for the instrumental polarisation-angle miscalibration). Four PDF-verified rows (singlerow_typebirefringence_beta_posterior), values taken verbatim from the measuring papers' arXiv abstracts: - Minami & Komatsu 2020, PRL 125, 221301, arXiv:2011.11254 — β = 0.35 ± 0.14° (Planck PR3; excludes 0 at 99.2% C.L., 2.4σ). The arXiv id is the measurement paper (NOT 2006.15982, the methodology-only companion that reports no β from data).
- Diego-Palazuelos et al. 2022, PRL 128, 091302, arXiv:2201.07682 — β = 0.30 ± 0.11° (Planck PR4 NPIPE; authors decline a cosmological significance pending foreground knowledge — caveat kept verbatim).
- Eskilt 2022, A&A 662, A10, arXiv:2201.13347 — β = 0.33 ± 0.10° (Planck PR4 LFI+HFI, frequency-independent all-bands, f_sky = 0.93).
- Eskilt & Komatsu 2022, PRD 106, 063503, arXiv:2205.13962 —
β = 0.342 (+0.094 / −0.091)° (Planck PR4 + WMAP 9yr joint; excludes 0
at 99.987% C.L., 3.6σ). The asymmetric posterior is stored as two
separate non-negative half-widths (
beta_err_lo_deg= 0.091,beta_err_hi_deg= 0.094) and is never abs()/symmetrised (sign / phase-boundary discipline at the attestation scale). - Parity-odd companion to the parity-even
cmb_polarisation_spectra(TE/EE/BB) andcmb_bispectrum(fNL) catalogs. EB/TB parity-odd power spectra are deferred — there is no cleanly-attestable published bandpower table with a clear license/URL/DOI; hand-keying a figure-read sample would fail attestation by construction. A future row_typebirefringence_ebtb_bandpowercan be added if a licensed machine-readable product becomes available. - Auto-discovered by the AMSC loader (no bridge code, no registration); the
per-row 9-field MPR attestation block is synthesised at read time from each
row's per-row source DOI +
entered_locally_at(deterministic, no live fetch). No new tool (describe()total stays 173 — a new catalog source, not aToolEntry); no packaging change (the attested data is auto-recursed bywheel.packages/packages=['srmech','siona']).
[0.5.0rc19] - 2026-05-29¶
rc19 of N for v0.5.0 — discoverable native-dispatch status. Closes #733
(the post-rc18 native-check recipe). Pure-Python; ABI unchanged at 3 (the
C header VERSION strings bump to rc19, SRMECH_ABI_VERSION does not).
- Discoverable native-dispatch status — top-level
srmech.native_status()(also insrmech.__all__/dir(srmech)) returning{has_native, dispatching, abi_version, expected_abi, native_version, load_error}, mirroringdescribe()['native']. The recipe-stable replacement for pokingsrmech.amsc._native.HAS_NATIVEin the TestPyPI-before-PyPI verification flow:dispatchingisTrueifflibsrmechloaded AND its ABI matchedEXPECTED_ABI_VERSION(native ops really run); on mismatch/failure it isFalse,load_errorcarries the reason, and srmech transparently uses the pure-Python fallback. NB the native shim lives atsrmech.amsc._native— NOTsrmech._native, the data dir that merely holds the binary. Framework reading: Class H (self-introspection) at package scale — the package recognising whether its own C backend is live. README native-dispatch recipe updated accordingly.
[0.5.0rc18] - 2026-05-29¶
rc18 of N for v0.5.0 — the downstream-wishlist + hygiene + perf CLEANUP rc.
No new surface; the rc17 SO(8) triality engine is carried forward verbatim
(the six bit-exact acceptance tests pass IDENTICALLY) with a performance fix,
doc/accuracy corrections from the downstream RBS-LM consumer wishlist, and
carry-over hygiene. Pure-Python only — no C source change; ABI stays 3 (the
C header VERSION strings bump to rc18, SRMECH_ABI_VERSION does not).
- Perf — the triality constants are now memoised.
srmech.qm.triality's internal_companion_maps(the dominant cost — 28512×128least-squares solves) plustriality_automorphism/triality_swapandsrmech.qm.so8'sg2_subalgebra/so8_adjoint_basis/so7_subalgebraarefunctools.lru_cache-memoised (the build runs once). Because callers may mutate the returned array, every public surface returns a DEFENSIVE COPY of the read-only cached build (the expensive build is cached; the per-call.copy()is cheap), so no mutable array is ever shared across callers and every returned value is bit-identical to a fresh build. The determinism (nonp.random) makes the cached value exact;octonion_table_attestationstays reproducible.tests/test_so8_triality.pydrops from ~300 s to ~1.5 s. (octonion_mult_tablewas already cached in rc17 — the exemplar pattern this rc extends to the so8/triality builders.) - W4 doc —
srmech.amsc.format.sha256_bytesreturns the HEX DIGEST. It is named for its INPUT (raw bytes) but returns the 64-char lowercase hex digeststr(the Python parity of Csrmech_sha256_hex/hashlib.…hexdigest()), NOT the raw 32-byte digest. Clarified in the docstring + the README Class-A row. No rename (route-through discipline). - W5 doc —
srmech.amsc.hdc.klein4_bundleaccepts ANY count. The docstring now states explicitly that it takes anyn >= 1(even OR odd); an exact tie (possible only for evenn) deterministically resolves to 0 for that bit. There is NO odd-only requirement (the "odd-only" note was a downstream artifact, never in srmech source). Mirrored into the ToolEntry summary. No validation added. - W6 code —
srmech.amsc._native.ABI_VERSIONback-compat alias (=EXPECTED_ABI_VERSION, currently 3) added + exported in__all__, for downstream code that reads_native.ABI_VERSION(the runtime-detectedNATIVE_ABI_VERSIONisNonewhen no native lib is present). Non-breaking. - W6b doc —
srmech.qm.sm.weak_mixing_anglereturns RADIANS. Docstring + ToolEntry summary now disambiguate the unit explicitly (the angle itself, NOTsin²θ_Wand NOT degrees; convert viamath.sin(…)**2). - W6c accuracy —
srmech.cosmosreferences. Nosrmech/cosmos/package exists; the packaged cosmos catalog issrmech.amsc.attested.cosmos_validation(Friedmann dark-fraction). The shipped surface (README +srmech/) has ZEROsrmech.cosmosreferences (already accurate); the only inaccurate references (rootCLAUDE.md, internal / not PyPI-shipped) were corrected to point at the real path. No packaged TE/EE/BB/fNL/lensing catalog (notebook-only). - W2 confirm — the
seedparam is already advertised.polar_randomandklein4_random's ToolEntries already expose the optional integerseed(rc13); confirmed, no change needed. - Hygiene — two
abs()float-tolerance spot-checks intests/test_so8_triality.pyswitched tocascade.magnitude(float(...))(full Class K∘C cascade-honesty, matching the file's_frobhelper); thepyproject.toml+pyproject-pure.tomldescriptionem-dash (which violated the files' own ASCII-only comment) swapped to-IDENTICALLY in both (byte-identical, under the 512-char Summary limit); thetests/test_llm_anthropic.pydocstring prose refreshed rc16 → rc18.
[0.5.0rc17] - 2026-05-29¶
rc17 of N for v0.5.0 — the SO(8) TRIALITY voxel. Three new srmech.qm-layer
surfaces make the so(8)/Spin(8) triality structure a callable, bit-exact-tested
surface (the full 28 = 𝔰𝔬(8) chiral read-out long flagged in [Unreleased]):
srmech.qm.octonion— the MPR-attested Cayley-Dickson-from-H octonion multiplication table (an(8,8,8)int8 structure-constant tensor whoseoctonion_table_attestation()content-addresses the table bytes viasrmech.amsc.format.sha256_bytes— no newhashlib.sha256) + theoctonion_left_mult/octonion_right_multL_a/R_abinders,octonion_conjugate, andoctonion_norm(Class K ∘ C, neverabs(): the scalar sum-of-squares is reduced throughsrmech.amsc.cascade.magnitude).srmech.qm.so8— the 28-generatorso(8)adjoint partitioned 14 (g2 = Der O) + 7 (L-type) + 7 (R-type):so8_adjoint_basis,g2_subalgebra(the 14 derivations; deterministic rank-revealing numpy subset, nonp.random),so7_subalgebra(the 21; theD4 → B3Z2 fold).srmech.qm.triality— the28×28order-3 outer automorphismτ = S_B · S_C(the PRODUCT of the two companion involutions, NOT a naiveA → Bmap) withFix(τ) = g2(dim 14) = the A-N1+3+7+3partition (theD4 → G2Z3 fold), the Z2 swap (Fix = so(7), dim 21), the Class-I8v → 8s → 8ccycle (viasrmech.amsc.cyclic.mod_add), the frame-transporttriality_apply, the Cartan companionstriality_companions, and the Class K ∘ Ctriality_relation_residual(neverabs()).
Six bit-exact acceptance tests (tests/test_so8_triality.py): τ³ = I /
τ ≠ I / τ² ≠ I; the KILLER Fix(τ) = g2 = 14 (belt-and-suspenders rank
asserts + bidirectional projection residual); Fix(Z2) = so(7) = 21; Cartan
residual = 0 over a g2/L/R sample; rep inequivalence + cycle closure; octonion
convention attested + reproducible. Residuals ≤ 4e-14.
+15 ToolEntries (158 → 173) — octonion_table_attestation gets its own
ToolEntry (the coverage walker demands one for every public srmech.qm.*
callable). operator_name __module__ hardening: a name that traverses
THROUGH a srmech module to a re-exported stdlib callable
(srmech.amsc.format.hashlib.sha256 → the real _hashlib sha256) is now
rejected by a post-resolution __module__ check. The PyPI README is refreshed
for BOTH the rc16 handle-grammar surface and the rc17 triality surface.
Pure-Python only — no C source change; ABI stays 3 (the C header's VERSION
strings bump to rc17, the SRMECH_ABI_VERSION integer does not).
Framework reading: the τ-fixed subalgebra of so(8) being exactly the 14 g2
derivations — the same 14 as the A-N 1+3+7+3 partition — is the keystone tying
the cascade vocabulary to the Spin(8) triality engine
(endianness ⊂ Class C ⊂ Klein-4 ⊂ Spin(8) triality). Class A (the attested
table), Class M (the L/R binders + g2 derivations + companions), Class C (the
Z2 swap + conjugation), Class I (the order-3 cyclic rep-permutation), Class
K ∘ C (the norm + residual, no abs()).
[0.5.0rc16] - 2026-05-29¶
rc16 of N for v0.5.0 — the "handle dual-grammar" voxel. rc15 marked the
7 srmech.spectral.* tools mcp_callable=False because their param/return
surface is a bare SpectralHandle (or SpectralHandle | bytes) — an
opaque, frozen, bytes-bearing dataclass JSON-RPC cannot carry by value.
rc16 carries it by reference: a producer's returned handle is
intercepted on the outbound path and emitted as a small tagged id object the
LLM copies verbatim into the next tool's input; a consumer param is resolved
back to the live object on the inbound path. The 7 spectral tools are now
mcp_callable=True (handle_pending 7→0; mcp_callable 151→158), and
chiral_dual's op is accepted as a dotted operator name (was an
over-advertised callable that bound the synth string "abs" then raised a
tolerated str-not-callable TypeError). Pure-Python only — no C source
change; ABI stays 3 (the C header's VERSION strings bump to rc16, the
SRMECH_ABI_VERSION integer does not).
Framework reading: name (meaning-encoded, biology-native / continuous-Hopf)
and uuid (position-encoded, silicon-native / cyclic-algebra) are two
grammars resolving to ONE in-process structure — the registry is the B/H/N
translation locus. We never force both halves into one "sentence"; each
consumer speaks the grammar native to it (SpectralHandle uses the uuid+name
registration arm; chiral_dual.op uses the stateless name arm).
Added¶
srmech/_handles.py— a package-scopeHandleRegistry(the shared name+uuid machinery, serving both the 7 spectral tools now and the bus later). Bounded-LRUOrderedDictkeyed byuuid.uuid4().hex(capHANDLE_REGISTRY_MAX=256),threading.RLock-guarded, with aname→uuidsecondary index and a value-hash idempotency map (an identical-by-value handle registered twice returns its existing id).kind-discriminated. TypedHandleNotFoundError(a clean "re-produce it" message on a miss/eviction) andHandleKindError. The$srmech_handleenvelope key +encode_envelope()/is_handle_envelope()helpers. A name auto-derives for aSpectralHandleas"spectral:" + content_sha[:12]— reusing the Class A SHA-256 already on the frozen dataclass, so no newhashlib.sha256call is introduced.resolve_operator_name(name)— the stateless name-grammar arm forchiral_dual'sop. Restricted to thesrmech.namespace: a name outside it (os.system,builtins.*, stdlib,numpy.*, …) is rejected with a cleanValueErrorbefore any import, so the advertisedoperator_namecontract is never "an arbitrary importable callable".tests/test_handles.py— registry dual-grammar (resolve by uuid AND by name), uuid/name disagreement, bounded-LRU eviction +HandleNotFoundError,HandleKindError, idempotent same-value registration, thread-safety smoke, and the operator-name allow-list (acceptssrmech.*, rejects everything else).tests/test_mcp.py—test_spectral_handle_param_coercer_resolves,test_spectral_handle_or_bytes_discriminates,test_operator_name_param_resolves_callable,test_evicted_handle_in_invoke_gives_clean_error(the empirical proof the rc14/15_identitypass-throughs are now real resolvers — the thing the statichas_coercerratchet structurally cannot see).tests/test_spectral.py— full JSON round-trips throughinvoke_tool:decompose → recompose(the wire form is asserted to be the$srmech_handleenvelope, NOT an inline coefficients dict), plus a chaineddecompose → predict → truncate_sparse → recomposeproducer→consumer chain.
Changed¶
- The 7
srmech.spectral.*ToolEntries flip tomcp_callable=True(the rc15mcp_callable=False+_SPECTRAL_HANDLE_PENDING_REASONmarkers removed). They are auto-included in both advertised catalogs (the MCPtools/listseam and the Anthropic catalog) anddescribe()re-buckets them fromhandle_pendingintomcp_callablefrom the live flags — no edit needed in those consumers. chiral_dual'sopparam typecallable→operator_name(a new declared type).srmech.mcp._coerciongains real coercers (_resolve_spectral_handle,_resolve_spectral_handle_or_bytes,_resolve_operator_name);serialise_nativegains oneSpectralHandlebranch (register + emit envelope) ahead of its dict/tuple fall-through._TYPE_LEXICON/_ENCODING_HINTteach the LLM the$srmech_handleenvelope (handle params) and the dotted operator-name string. Thecallablecoercer key is RETAINED (other tools / the DSL / direct callers still pass a live callable; the exhaustiveness ratchet needs the key).chiral_dual's Python signature is unchanged — resolution is at the coercion layer, so the DSL + direct callers are unaffected.- The rc15 catalog-EXCLUSION ratchets are INVERTED to catalog-INCLUSION
in BOTH
tests/test_mcp.py(test_handle_pending_absent_from_advertised_catalogs) ANDtests/test_llm_anthropic.py(test_handle_pending_tools_excluded_from_anthropic_catalog+test_tool_catalog_includes_every_advertised_tool'sexpected + 7→+ 0): zero handle-pending tools remain; all 7 spectral names are PRESENT in both advertised surfaces. The every-tool invocation smoke (_synth_value_for_type) now synthsoperator_name→srmech.amsc.cascade.chiral_flip(a genuine unary seq→seq op) andSpectralHandle→ a freshly-minted registered$srmech_handleenvelope, so the smoke exercises a REAL round-trip rather than a tolerated domain failure.
Unchanged¶
- C source + ABI. No file under
c/other than the header's VERSION strings is touched;SRMECH_ABI_VERSIONstays 3 and the Python shim'sEXPECTED_ABI_VERSIONstays 3. The JPL Power-of-Ten ratchet is unaffected. The whole voxel is a Python tool-schema / registry / MCP-surface change. TestPyPI rc verification before any production PyPI tag.
[0.5.0rc15] - 2026-05-29¶
rc15 of N for v0.5.0 — the "every-tool invocation smoke + honest
mcp_callable marking" voxel (upstream §10.1). rc14 made every declared
param TYPE JSON-coercible and shipped the static has_coercer ratchet —
but has_coercer could not tell a REAL coercer from the _identity
pass-through. The 7 srmech.spectral.* tools whose surface is a bare
SpectralHandle / SpectralHandle | bytes went statically-green yet
were not actually invocable across the JSON boundary (an opaque
in-process dataclass handle cannot ride JSON by value). rc15 closes that
gap empirically and marks those 7 honestly. Pure-Python only — no C
change, ABI stays 3.
Framework reading: the package declaring its own callable shape (which tools are advertisable vs. handle-pending) IS Class H (self-introspection) at package scale — the apparatus thesis. No new primitive class is introduced.
Added¶
- THE EVERY-TOOL INVOCATION SMOKE (§10.1) —
test_every_advertised_tool_invocableintests/test_mcp.py. For EVERYmcp_callable=TrueToolEntry, it synthesises a minimal valid args dict from the tool's schema (using the rc14 coercion encodings per declared type —int→1,float→1.0,bytes→base64(b"abcd"),np.ndarray→2×2 identity,complex→[1.0, 0.0], containers→minimal valid shapes), then actually CALLS the tool viainvoke_tool. It asserts NO binding error (TypeErrorunexpected/missing kwarg) and NO coercion error; it TOLERATES domain errors (ValueError/ non-square matrix / length mismatch / op-internalTypeError) — those prove the tool was reached with bindable + coercible args (callability, not domain validity). Result: 151/151 advertised tools invocable. This is the EMPIRICAL complement to rc14's statichas_coercerratchet — it closes thehas_coercer-vs-actually-callable gap that left the SpectralHandle_identitypass-through green. ToolEntry.mcp_callable: bool(defaultTrue, back-compat) +ToolEntry.mcp_unavailable_reason: str | None. The 7 handle-pendingsrmech.spectral.*tools (decompose/delta/recompose/similarity/predict/prediction_error/truncate_sparse) are markedmcp_callable=Falsewith the reason "handle-pending: by-reference SpectralHandle id arrives in the bus handle-grammar (rc16); use the srmech package directly until then."to_jsonable()emits both new fields.test_handle_pending_absent_from_advertised_catalogs+test_handle_pending_tools_excluded_from_anthropic_catalog. Assert the 7 handle-pending tools are absent from BOTH advertised catalogs (MCPtools/list+ Anthropic_build_tool_catalog) while remaining in the registry for introspection.
Changed¶
- Advertised-surface exclusion of
mcp_callable=Falseentries at BOTH seams.srmech.mcp._tools.tool_entries_to_mcp_defs(the MCPtools/listsource) andAnthropicAgent._build_tool_catalog(the Anthropic seam) now skipmcp_callable=Falseentries, so an LLM is never offered a tool it cannot call. The advertised surface drops from 158 → 151; the registry (get_tool_schema().tools) keeps all 158 for introspection. srmech.introspect.describe()reports the split. Thetoolsblock now carriesmcp_callable+handle_pendingcounts alongsidetotal+by_category, and a top-levelhandle_pendinglists the 7 names. ThedescribeToolEntry's documented return-shape is updated to match.- Schema-accuracy fix surfaced by the smoke:
srmech.amsc.laplacian.dense_laplacian+normalized_laplaciannow declareedgesaslist[tuple[int, int]](matching the shipped(n, edges: Iterable[Tuple[int, int]])signature + the siblingdense_adjacencyentry); the earlier barelisttype advertised an edge-list shape too loose for an LLM to populate. Signature unchanged.
Deferred¶
- SpectralHandle by-reference invocation → rc16 (per user decision).
The bus handle-grammar (a
SpectralHandleid arriving by reference) lets the 7 spectral tools becomemcp_callable=Truethen; until then thesrmechpackage (import srmech.spectral) is the path for spectral work.
[0.5.0rc14] - 2026-05-29¶
rc14 of N for v0.5.0 — the "full JSON↔native MCP coercion" voxel. A
live probe of the rc13 catalog proved 65 of 158 tools (41%) were
advertised to MCP / Anthropic but UNCALLABLE — their parameters are
bytes / np.ndarray / complex, types JSON-RPC cannot express. rc14
makes all 158 tools MCP-callable via full bidirectional coercion:
a tool that accepts JSON but returns an un-serialisable ndarray is equally
unusable, so the fix covers params-in AND results-out. Pure-Python only —
no C change, ABI stays 3.
Framework reading: bidirectional coercion is Class H
(self-introspection) at the package's tool-surface — the package making
its own A–N callables' types JSON-expressible across the JSON-RPC /
Anthropic boundary. The base64 / [re, im] encodings are Class B
(TLV-framing) — encoding-boundary translation between continuous native
types and the discrete JSON wire. No new primitive class is introduced.
Added¶
- Bidirectional JSON↔native coercion (
srmech.mcp._coercion). A clean type→coercer dispatch keyed on eachToolParameter.typestring, plus a structural outbound serialiser: bytes↔ base64str(user decision — binary-safe + unambiguous; replaces the rc13 hex convention). Malformed base64 raises a clearValueErrornaming the param.np.ndarray↔ nested JSON list (row-major.tolist()); complex arrays serialise each element as[re, im].complex↔[re, im](a bare JSON number decodes tocomplex(n, 0)).- numpy scalars (
np.int64/np.float64/np.uint8/ …) → Pythonint/float/boolon the outbound path. - container types recurse element-wise:
Sequence[bytes],Sequence[np.ndarray],tuple[np.ndarray, ...],Mapping[bytes, bytes](template.render),list[tuple[bytes, int]](dispatch.matchrules),list[tuple[bytes, bytes]](naming.lookuppairs). - Schema encoding hints. Each non-JSON native type's schema property
carries a JSON-encoding hint in its
description(bytes→ string + "base64-encoded bytes";np.ndarray→ array + "nested JSON array, row-major; complex elements as [re, im]";complex→ array + "[real, imaginary]"; containers → the element hint), so an MCP / Anthropic consumer learns the wire form up front.complexnow renders as a JSON-schemaarray(wasstring). The rc13 property-key grammar + rc10 name discipline are untouched (the hint lives in the value's description, never the key). - THE TYPE-COERCIBILITY RATCHET —
test_all_param_types_json_coercibleintests/test_mcp.py. For EVERYToolEntryparam acrossget_tool_schema().tools, it asserts the coercion dispatch HAS an explicit handler for the declared type. This guarantees no future tool can advertise an uncallable param type unnoticed (complements the rc13 schema/signature name-drift ratchet). Running it over all 158 tools surfaced 33 distinct param types, all now handled.
Changed¶
srmech.mcp._tools._coerce_argumentsnow routes every inbound argument through_coercion.coerce_param(was a small inline hex/path matcher).srmech.mcp._tools.serialise_resultnow walks the result through_coercion.serialise_nativebeforejson.dumps, so bytes → base64, ndarray → nested list (complex as[re, im]), complex →[re, im], numpy scalars → Python scalars, tuples/sets → lists — round-trippable with the inbound path.
Tests¶
test_coercion_roundtrips_scalar_leaf_types—coerce_param(serialise_ native(x)) == xfor bytes / complex / real-ndarray (exact); complex arrays via the explicitcomplex_pairs_to_ndarraybuilder.test_serialise_native_emits_json_serialisable—json.dumpssucceeds for bytes / complex / real+complex ndarray / numpy scalars / nested tuples / dict-with-bytes-key.test_invoke_*live-path round-trips throughinvoke_tool(the shared MCP + Anthropic entry):naming.lookup(base64 key + pairs),template.render(base64 mapping),hdc.bind(base64 ↔ bytes round-trip),laplacian.jacobi_eigvals(nested-list matrix → eigenvalue list),qm.sm.higgs_potential(phi=[re, im]),dispatch.match(base64 rules) — each asserts the result is JSON-serialisable.test_invoke_klein4_random_seed_reproducible_rc14_path— rc13 seed reproducibility holds through the rc14 ndarray-result coercion.test_schema_renders_encoding_hints+test_encoding_hints_preserve_property_key_grammar— STEP 3 schema-hint coverage; property-key grammar still clean.- The two rc13 hex-based tests (
*_sha256_bytes_*,serialise_result_ handles_bytes_*) are updated to base64 (the rc14 wire form). - Version-gate test renamed
test_version_is_0_5_0rc13→test_version_is_0_5_0rc14;test_version_module_matchesstays literal-free.
NOTE — arrays over JSON are payload-heavy. Arrays are now MCP-callable, but a large ndarray serialised as a nested JSON list is expensive over the JSON-RPC wire. The bus handle-API (a later voxel) remains the by-reference path for bulk array work — per the package-for-bulk / MCP-for-interactive design boundary. Use MCP coercion for interactive / small-payload calls; use the bus handle for bulk spectral arrays.
[0.5.0rc13] - 2026-05-29¶
rc13 of N for v0.5.0 — MCP surface-correctness bug-fix voxel. Two
real bugs surfaced by upstream MCP usage, plus the would-have-caught-it
ratchet. Both bugs affected BOTH the MCP path and the Anthropic adapter
(shared srmech.mcp._tools.invoke_tool dispatch). Pure-Python only — no
C change, ABI stays 3.
Framework reading: the fixes are Class H (self-introspection) at the
package's tool-surface — the schema recognising its own callables' shape.
The integer seed is Class N (rational / deterministic anchor) made
JSON-expressible so the bit-exact / attestation discipline survives the
JSON-RPC / Anthropic boundary. No new primitive class is introduced.
Fixed¶
hdc.klein4_random/hdc.polar_randomare now seedable over JSON-RPC (BUG A — found via upstream MCP usage). They were seedable only viarng: numpy.random.Generator, which cannot cross JSON-RPC nor be expressed in an Anthropic tool schema — so MCP / Anthropic callers could not obtain a DETERMINISTIC Klein-4 / polar vector, breaking srmech's bit-exact / attestation discipline. Both ops gain an integerseed: int | None = Noneparam: when given (andrngis not), the generator is built internally asnp.random.default_rng(seed). The in-processrng=path is preserved for back-compat; precedence — an explicitrngwins overseedif both are supplied. Each op'sToolEntrynow advertises the JSON-friendlyseed: intand DROPS the un-serialisablerngGenerator (a Generator has no valid JSON-Schema type and should never have been in the MCP-facing schema). Acceptance:klein4_random(seed=42)is bit-identical twice directly AND twice throughinvoke_tool.srmech.amsc.naming.lookupis callable again via MCP / Anthropic (BUG B — found via upstream MCP usage). ItsToolEntrydeclared a paramentriesthat the shippedlookup(key, pairs)does not accept, so every MCP / Anthropic invocation raisedTypeError: lookup() got an unexpected keyword argument 'entries'— the tool was uncallable. Fixed the SCHEMA to match the shipped signature (entries→pairs), least-surprise (the function is the SSoT).srmech.amsc.template.renderschema/signature drift (surfaced by the new ratchet). ItsToolEntrydeclaredsubstitutionswhile the shippedrender(template_bytes, mapping)acceptsmapping— same uncallable-tool failure mode as BUG B. Aligned the schema (substitutions→mapping).
Added¶
- THE RATCHET — schema/signature alignment test
(
test_schema_signature_alignment_no_driftintests/test_mcp.py). For EVERYToolEntryinget_tool_schema().tools, it resolves the dotted callable and asserts each declared parameter is BINDABLE to the callable's signature (tolerating**kwargsand the rc10*argsclean-name convention). This catches the whole class of bug behind BUG B before it ships. Running it surfaced exactly two drifts across all 158 tools —naming.lookup(entries) andtemplate.render(substitutions) — both now fixed; the ratchet passes clean.
Tests¶
test_klein4_random_seed_reproducible+test_polar_random_seed_ reproducible— sameseed⇒ bit-identical vectors directly and viainvoke_tool; a different seed differs (seed is load-bearing).test_random_ops_rng_takes_precedence_over_seed—rngwins overseed; the legacyrng=path stays back-compat.test_random_ops_schema_drops_unserialisable_rng— the*_randomToolEntries advertiseseedand NOTrng.test_naming_lookup_callable_via_invoke_tool+test_template_render_callable_via_invoke_tool— both return a real result throughinvoke_tool(no TypeError).- De-brittled version-gate test carries forward —
test_version_is_0_5_ 0rc13(renamed) is the single deliberate human-literal gate;test_version_module_matchesstays literal-free.
[0.5.0rc12] - 2026-05-29¶
rc12 of N for v0.5.0 — the "DSL surface" voxel. Exposes the rc8
cascade-composition DSL (srmech.dsl.*) as declarative MCP / Anthropic
ToolEntrys, so an LLM composes AND runs a cascade in a single tool
call. The fluent chain().then(...).loop(...) builder is not
tool-callable (a tool call can't chain methods); the declarative surface
does real work in ONE call. Pure-Python only — no C change, ABI stays 3.
Framework reading: the DSL composes Class M (cross-class bind) over
the cascade catalog — each chain stage is one A–N primitive-class
instance, the chain is the composition. list_catalog_ops is Class E
(catalog enumeration) ∘ Class F (descriptor render). No new primitive
class is introduced; this voxel makes the rc8 composer callable in one
shot from an LLM tool surface.
Added¶
srmech.dsl.run_toml_chain(spec, input_value)— compose + run a cascade in ONE call. Author an inline TOML chain spec (a[chain]table +[[stage]]array; each stage carries one discriminator —op/loop_n+sub_chain/fold_init+fold_op/reduce_op), feed an input value, get the chain result. The declarative, one-shot face of the rc8 DSL. Registered as aToolEntry(ownersrmech, categorydsl) with plain keyword params (spec/input_value), so the rc10 property-key grammar holds andinvoke_tool'sfn(**coerced)calls it directly (no VAR_POSITIONAL unpack).srmech.dsl.list_catalog_ops()— enumerate the cascade-catalog ops. Returns one record per op ({name, class, purpose}) sourced from the on-disk cascade-catalog TOML descriptors (the SSoT), so an LLM can pick validop/fold_op/reduce_opnames + read each op's A–N class before authoring a spec. 8 ops:best_rational_signed,chiral_dual,chiral_flip,cyclic_gcd,magnitude,net_chirality,pin_slot_at_zero,reorient. Registered as a no-paramToolEntry.srmech.dsl.build_chain_from_toml_str(spec)— the string counterpart ofbuild_chain_from_toml. Builds aChainfrom an in-memory TOML chain-spec string (rather than a path), so a chain can be authored inline and materialised without writing a file first. The load-bearing primitiverun_toml_chainis built on.
Changed¶
srmech.amsc.tool_schema.warmup_all()now importssrmech.dsl. Appended to the warmup import list so the dslToolEntrys register no matter the entry-path and the manifest stays complete. The registration itself fires from_register_dsl_tools()attool_schemaimport (declarative data only — it does NOT importsrmech.dsl, so there is no import cycle; the dotted-name targets are resolved bysrmech.mcp._toolsat invoke time). Thewarmup_all()import is independently verified cycle-free: neithersrmech.dslnorsrmech.introspectimportssrmech.amsc.tool_schemaat module load.- De-brittled version-gate test carries forward —
test_version_is_0 _5_0rc12(renamed) is the single deliberate human-literal gate;test_version_module_matchesstays literal-free (sources-agree + PEP 440 shape only).
Tests¶
- Test determinism: pinned
time_nson all bio-totp round-trip tests so they no longer depend on wall-clock window alignment (eliminates a CI-load timing flake; no production change).
[0.5.0rc11] - 2026-05-29¶
rc11 of N for v0.5.0 — the "Self-recognition root" voxel. The keystone of the v0.5.0 substrate-self-recognition arc: the package gains a single canonical surface to populate AND recognise its own tool-schema shape. Pure-Python only — no C change, ABI stays 3.
Framework reading: describe() IS Class H (self-introspection) at
package scale — the package recognising and rendering the SHAPE of its
own A–N tool surface. warmup_all() is the Class A (content-addressed
callable identifier) ∘ Class E (catalog) population step that
GUARANTEES the Class H view is complete regardless of entry-path.
Added¶
srmech.amsc.tool_schema.warmup_all()— THE single registration entry-point. Imports every submodule that registersToolEntrys (srmech.bus/srmech.introspect) so the registry is fully populated no matter how srmech was entered (library / CLI / MCP / Anthropic adapter). Idempotent. Fires fromsrmech.__init__(per user direction 2026-05-29 — substrate-coherent: every consumer sees the complete tool-schema from t=0). Permanently closes the orphan-registration bug class — the rc9 miss wheresrmech.bustools were silently absent from the LLM-facing catalog because no entry-path imported the bus. THE single place future voxels add their registration import. Re-exported assrmech.warmup_all.srmech.introspect.describe()— the self-recognition ROOT surface. The "what is srmech?" root: a structured, at-a-glance map of the package's own shape — package version, tool-schema version, native-dispatch status (has_native/abi_version/native_version), total registered tool count + per-category breakdown, and the sorted list of category names. Callswarmup_all()first so the counts are complete. Registered as aToolEntry(srmech.introspect.describe, no params) so MCP / Anthropic consumers can ask "what is srmech / what can it do?". A ROOT / INDEX — it surfaces the SHAPE; per-tool JSON schemas, env, and error-type detail come from later voxels (rc15 / rc16).
Changed¶
srmech.mcp._toolsnow warms up via the canonical entry-point. The rc9 scattered side-effect imports (from .. import bus/introspect) are replaced by a singlewarmup_all()call. Behaviour is identical (registry fully populated beforeget_tool_schema()), but the warmup list is now maintained in ONE place.- De-brittled the version-gate test (
tests/test_signal_processing _scaffolding.py).test_version_is_0_5_0rcN(renamed to…rc11) is now the SINGLE deliberate human-literal gate — the conscious per-rc bump point.test_version_module_matchesno longer hardcodes a literal; it only asserts the SSoT sources AGREE plus a PEP 440 sanity shape, so it survives version bumps (this gate bit us 3× in rc10).
[0.5.0rc10] - 2026-05-29¶
rc10 of N for v0.5.0 — two shared-dispatch bug-fixes found by a LIVE Anthropic API test of the rc9 adapter (the mocks could not catch them).
Both bugs lived in the shared tool-schema / dispatch surface that
both the MCP adapter (rc6, srmech-mcp) and the Anthropic SDK
adapter (rc9, srmech-agent) route through, so each affected both
transports at once. Pure-Python only — no C change, ABI stays 3.
Fixed (load-bearing, both adapters)¶
- Illegal tool-schema property key blocked the ENTIRE Anthropic
catalog (live 400).
srmech.amsc.hdc.polar_bundleandsrmech.amsc.hdc.klein4_bundlewere registered with the param name*vectors— the Python varargs sigil leaked into theToolParameterNAME, so the generatedinput_schema.propertiescarried a key*vectors. Anthropic rejects it with400 — input_schema.properties: Property keys should match pattern '^[a-zA-Z0-9_.-]{1,64}$', which fails the wholemessages.createcall (every tool, not just the two bundles). The unit-test mocks never sent the catalog to a real validator, so they passed; only a live API call surfaced it. Fix: rename the param*vectors→vectorsin both registrations, typedSequence[np.ndarray]to match the siblingsrmech.amsc.hdc.bundleconvention (variadic — "one or more vectors of equal length"). - Varargs dispatch (
fn(**coerced)cannot call a*argsfunction).srmech.mcp._tools.invoke_toolended withreturn fn(**coerced)(keyword-args only). For a variadic callable likepolar_bundle(*vectors),fn(vectors=[...])raisesTypeError: polar_bundle() got an unexpected keyword argument 'vectors'. Both adapters call thisinvoke_tool, so invoking either bundle tool was broken on BOTH paths even after the property-key rename. Fix: detect aVAR_POSITIONALparameter viainspect.signature(fn)and unpack the supplied sequence positionally (fn(*seq, **coerced)); the historical sigil-prefixed key (*vectors) is still tolerated so no in-flight caller breaks.
Added — defence-in-depth + regression ratchets¶
- Property-key sanitiser in the MCP/Anthropic converter.
tool_entry_to_mcp_defnow strips any leaked Python sigil (leading*/**) and clamps everyinputSchema.propertieskey to^[a-zA-Z0-9_.-]{1,64}$(therequiredlist is sanitised in lockstep so it still references real properties). The rename above is the SSoT fix; this guards BOTH adapters against a future sloppy varargs/kwargs registration (mirrors the rc9 belt-and-braces lesson). - Two would-have-caught-it test ratchets in
tests/test_mcp.py: (a) for every registered tool, assert everytool_entry_to_mcp_def(entry)["inputSchema"]["properties"]key matches the Anthropic grammar and contains no*(caught BUG 1 — it FAILS on the pre-fixtool_schema, PASSES after the rename; guards all 155 tools on both transports forever); (b) invokesrmech.amsc.hdc.polar_bundleANDsrmech.amsc.hdc.klein4_bundlethroughinvoke_toolwith a small valid list of vectors and assert a real bundled result (not aTypeError/MCPToolError) (caught BUG 2).
For Claude Code users on the rc6 MCP path, this rc fixes the two bundle tools (they were uncallable / catalog-blocking there too); no other behaviour changes.
[0.5.0rc9] - 2026-05-28¶
rc9 of N for v0.5.0 — Anthropic SDK secondary adapter (optional) + POSIX bus-discovery fix.
Fixed (load-bearing, since v0.5.0rc1)¶
- POSIX bus discovery silently returned empty.
_iter_candidate_filesfiltered the~/.srmech/directory withPath.is_file(), which returnsFalsefor AF_UNIX socket files (they'reS_IFSOCK, notS_IFREG). Effect:by_name()/list_endpoints()reported no live endpoints on POSIX even though the socket existed and was connectable, so every test using the_wait_for_endpointhelper hit a 5 s timeout on every POSIX CI cell across the rc1–rc9 series (40+ failing tests per cell, masked by the prior 3 POSIX cells going red the whole time). Windows passed throughout because its.txtregistry file IS a regular file. Fix: invert the filter (skip directories; accept regular files + sockets). New regression testtest_discovery_iterates_uds_socket_files. Endpoint.stop()left accepted-client sockets open. The listen socket was closed but each per-connection worker held its own accepted socket — workers would keep serving requests that arrived AFTERstop()returned, defeating the "stopped server cannot reply" contract verified bytest_send_after_server_stop_raises. Surfaced on POSIX once the discovery bug above was fixed (was previously masked by the pre-_wait_for_endpointtimeout). Fix: track each acceptedConnectionon a per-endpoint list and close them all instop()before joining worker threads.
Added — Anthropic SDK adapter¶
Optional companion to rc6 MCP (primary path for Claude Code users). This
adapter targets users who script Claude API directly outside Claude Code
(FastAPI servers, Jupyter notebooks, CI pipelines using the anthropic
Python SDK).
- Added:
srmech.llm.anthropic_agent.AnthropicAgent— builds the tool catalog fromsrmech.amsc.tool_schemaToolEntries, hands to Anthropic SDK, runs the tool_use message-loop, returns final assistant message with per-tool-call MPR attestation transcript. - Added:
srmech-agentconsole-script entry.pip install srmech[anthropic]installs the optional dep + thesrmech-agentcommand. - Optional dep:
anthropic>=0.40.0. Default install does NOT add it (zero impact on users who don't need it). - The Anthropic tool-name grammar (
^[a-zA-Z0-9_-]{1,64}$) doesn't admit the dots in srmech dotted names; the adapter swaps.↔_round-trip and keeps a per-instance reverse map so any future tool with an underscore in its name still round-trips unambiguously. - Re-uses
srmech.mcp._server.build_attestationso the MPR envelope per tool call is byte-identical to what the MCP adapter emits for the same (tool, result) pair — same response_sha256 across transports.
For Claude Code users, this rc adds nothing — rc6 srmech-mcp is still
the right path. This rc exists for the user community: anyone scripting
Claude API outside Claude Code can now use srmech as a tool source with
the same MPR attestation discipline.
Pure-Python; ABI unchanged at 3. ~20 new tests using mocked Anthropic client (no real API calls).
This completes the v0.5.0 rc walk (rc1-rc9 all shipped). Awaiting user load-test signal before cutting clean v0.5.0 → production PyPI.
Fixed — MCP tool-schema discoverability gap¶
- MCP tool-schema discoverability fix:
srmech.introspect.list,srmech.introspect.by_pid,srmech.bus.list_endpoints,srmech.bus.by_name, andsrmech.bus.decode_spliceare now visible to MCP / Claude Code consumers. Root cause:srmech.mcp._toolsonly importedsrmech.amsc.tool_schemaand never triggered the side-effect imports ofsrmech.bus._tool_schemaorsrmech.introspect, so the "status" introspection surface and the bus discovery surfaces were silently missing from the LLM-facing catalog. Fix adds explicitfrom .. import bus as _bus/from .. import introspect as _introspectwarmup at the top ofmcp/_tools.pyplus two new ToolEntry registrations on each module (introspect:list,by_pid; bus:list_endpoints,by_name). Total registered tool count goes from 150 to 155 (decode_splice was registered all along — it just wasn't visible through the MCP wrapper because the side-effect import was missing). Six new regression tests intests/test_mcp.pylock the fix in by asserting each tool appears inget_tool_schema()after importingsrmech.mcp._tools(the exact path Claude Code / MCP clients take).
[0.5.0rc8] - 2026-05-28¶
rc8 of N for v0.5.0 — Cascade DSL runner (task #235).
Fluent chain() API + TOML-driven runner + loop/fold/reduce control flow.
- Added:
srmech.dslmodule —chain(name).then(op).loop(n, sub).fold(init, op).reduce(op).run(input)fluent composition over the 8 cascade-catalog ops shipped in v0.4.5. - Added: TOML cascade-catalog runtime loader — reads the 8 descriptors
from
srmech/amsc/_research/cascade_catalog/and resolves op names to Python entry points (which route to C peers whenHAS_NATIVE). - Added:
srmech dsl run / ops / visualizeCLI subcommands. Therunsubcommand loads a TOML chain spec ([chain]+[[stage]]array withop/loop_n+sub_chain/fold_init+fold_op/reduce_opdiscriminators), executes against--input(inline JSON) or--input-file(JSON / NDJSON), emits to stdout or--output-file. - DSL stages emit
dsl.<chain_name>.stage.<N>events (and a closingdsl.<chain_name>.completeevent) when introspection publish is active; observable viasrmech statusorsrmech bus tap. - Each builder method (
then/loop/fold/reduce) validates the op name against the on-disk catalog at chain-construction time (unknown ops raiseValueErrorimmediately, not atrun()time). - No new primitive class — loop / fold / reduce are compositions of the existing 14-class A–N vocabulary (loop = Class I cyclic repetition; fold / reduce = Class M accumulator bind).
Completes ADR-0002 Phase 2-v2 (loop/fold/reduce in chain DSL; task #235).
Pure-Python; ABI unchanged at 3. ~30 new tests; full suite ~1644 passing.
Remaining: optional rc9 Anthropic SDK adapter (defer if you don't need non-Claude-Code LLM integration).
[0.5.0rc7] - 2026-05-28¶
rc7 of N for v0.5.0 — UTLP Bio-TOTP cipher alignment + tool-schema opt-in discoverability.
Two related fixes per user direction 2026-05-28:
-
UTLP Bio-TOTP cipher alignment (Claim 255). rc3 shipped a SHA-256 chained-cipher that was structurally related but DIFFERENT from the actual UTLP Bio-TOTP pattern in
examples/utlp/utlp_hal_security.h. rc7 replaces rc3's_chain.pywith_bio_totp.pyimplementing the real UTLP construction: key derivation rolls with a 250 ms time bucket (Key = SHA256(DNA || QuantizedTime)[0:16]); receiver tolerates ±1 window for clock skew; nonce constructed from sender_id + channel_id + packet_seq ("Exon fields"); same code path for unencrypted (ZERO_DNA) and encrypted channels (herd-immunity); kwarg renamedseed→dnafor naming alignment (seedstill accepted with DeprecationWarning). Default cipher uses stdlib HMAC-SHA-256 keystream (zero new deps);pip install srmech[crypto]opts into UTLP-exact AES-128-CTR via thecryptographylibrary. -
Tool-schema opt-in discoverability: every emitting op's ToolEntry now mentions inline: "Events emitted only when wrapped in
srmech.introspect.publish()orSRMECH_PUBLISH_STATUS=1env-var set; otherwise silent." Plus new top-levelsrmech.introspect.publishToolEntry documenting the opt-in. Discoverable via the MCP adapter (rc6) — LLMs in Claude Code see the opt-in path inline when reading the tool catalog.
OUT OF SCOPE per user direction: UTLP's mesh / multi-arbor / Loom / Genesis-election / time-sync layer is embedded-specific and is NOT brought over to srmech.bus. srmech.bus is local-IPC only.
Pure-Python; ABI unchanged at 3. ~30 new tests; full suite ~1614 passing.
Remaining v0.5.0 rcs: rc8 DSL runner (task #235), optional rc9 Anthropic SDK secondary adapter.
[0.5.0rc6] - 2026-05-28¶
rc6 of N for v0.5.0 — MCP server adapter (Claude Code integration).
LOAD-BEARING for the LLM tool-schema endpoint goal per user direction 2026-05-28. User is on Claude Code Max plan (no Anthropic API/SDK tier); MCP (Model Context Protocol) is the primary LLM integration path.
- Added:
srmech.mcpmodule — MCP server exposingsrmech.amsc.tool_schemaToolEntries (~150) as MCP tools. JSON-RPC 2.0 over stdio (subprocess mode) or HTTP+SSE (cross-process mode). - Added:
srmech-mcpconsole-script entry.pip install srmechnow installs bothsrmechandsrmech-mcpon PATH. - Three Claude Code usage modes supported by SAME adapter:
- Pure local stdio subprocess (Claude Code default)
- Cross-terminal observability (long-running sweep + LLM in another
terminal connects via
srmech-mcp --bus-endpoint sweep-NAME) - Subagent-orchestrated research (subagent runs srmech-mcp; reports back via Claude Code subagent return)
- Each MCP tool-call response carries MPR attestation (response_sha256 + parser_version + tool_name + timestamp).
- Inherits rc3 state-chained wire format when proxying via bus
(
--bus-endpointmode): LLM connections are forward-secure by construction.
Pure-Python; ABI unchanged at 3. ~31 new tests; full suite ~1584 passing.
Composes with: rc4 CLI (srmech bus tap NAME can be used to observe
what tools the LLM is calling); rc5 async wrapper (srmech-mcp HTTP+SSE
uses async).
Remaining v0.5.0 rcs: rc7 DSL runner (task #235), rc8 optional Anthropic SDK secondary adapter.
[0.5.0rc5] - 2026-05-28¶
rc5 of N for v0.5.0 — async wrapper for the bus.
Thin asyncio shim over the sync API via asyncio.to_thread(). Covers
FastAPI/aiohttp/asyncio-native callers without doubling C-peer complexity.
Sync API remains the SSOT; async is a courtesy wrapper.
- Added:
srmech.bus.aiomodule —AsyncChannel,AsyncEndpoint,AsyncPipeHandle, asyncconnect, asyncserve, asynclist(alias forlist_endpoints), asynclist_endpoints, asyncpipe. - Handler can be sync OR async; async handlers awaited via
asyncio.run_coroutine_threadsafefrom the sync server's worker thread (handler runs back on the caller's event loop, not in the worker thread). aio.connect/aio.serveare async context managers (async with ...).subscribe()returns an async generator (per-__anext__worker hop).- All encryption/seed/discovery semantics inherited unchanged from sync API.
Pure-Python; ABI unchanged at 3. No native asyncio plumbing in v1 (real
asyncio-native path via asyncio.open_unix_connection can come in v0.5.x
if a workload demands).
~25 new tests using asyncio.run(...) inside sync test functions (no
pytest-asyncio dep added — keeps the test surface light).
[0.5.0rc4] - 2026-05-28¶
rc4 of N for v0.5.0 — srmech bus CLI subcommands.
Adds list / tap / pipe / send / serve subcommands to the srmech
console-script entry that was introduced in v0.4.6. Operates the
v0.5.0 bus from the shell: enumerate endpoints, tail live event
streams, chain endpoints, one-shot send, test-serve.
- Added:
srmech bus list [--json] [--all]— active endpoints, ownership-filtered. - Added:
srmech bus tap NAME [--seed HEX] [--format json|pretty] [--filter TYPE] [--limit N]— stream events. - Added:
srmech bus pipe SRC DST [--seed-src HEX] [--seed-dst HEX] [--transform PY_EXPR]— daemon pipe. - Added:
srmech bus send NAME EVENT_JSON [--seed HEX] [--timeout S] [--stdin]— one-shot request. - Added:
srmech bus serve NAME [--echo] [--seed HEX] [--seed-mint] [--handler-module PYMOD:func]— test server.
Pure-Python; ABI unchanged at 3.
~30 new tests via subprocess invocation of the entry point.
[0.5.0rc3] - 2026-05-28¶
rc3 of N for v0.5.0 — state-chained wire format ("biological TOTP-like").
Per user direction 2026-05-28: same-user-defensive forward-secrecy for bus channels. Each frame N's encoding depends on state_{N-1}; receiver must walk the chain to decrypt. Pure-Python cipher (SHA-256 keystream + HMAC-SHA256 integrity); ABI unchanged at 3 (no new C symbols this rc).
Framework reading: Class A + Class I + Class K composed at the wire layer; substrate-self-recognition extended to the frame chain.
- Added:
srmech.bus._chain—ChainState(per-direction cipher state),derive_state,decode_splice(pure-function decoder for tool-schema / LLM introspection). Cipher:state_0 = sha256(seed || ":" || channel_id || ":" || direction);keystream_N = sha256(state_N || "ks" || counter_be8 || block_be4);ciphertext_N = plaintext XOR keystream;state_{N+1} = sha256(state_N || "st" || ciphertext_N);mac_N = hmac_sha256(state_N || "mac", ciphertext || counter_be8)[:16]. Domain- separation tags ("ks"/"st"/"mac") defend against keystream / state-advance / MAC-key cross-contamination. - Added:
srmech.bus._tool_schema— registerssrmech.bus.decode_spliceas aToolEntryfor LLM consumption (load-bearing for rc5 MCP adapter; LLMs can introspect the cipher). - Added:
srmech.bus._seed— seed-resolution cascade module (resolve_client_seed/resolve_server_seed/mint_and_write_seed/discard_seed_file). Three sources, in priority order: explicit kwarg →SRMECH_BUS_SEEDenv var →~/.srmech/bus-{name}.seed0o600 file. - Changed:
connect(name, seed=...)andserve(name, seed=..., handler=...)accept an optional pre-shared seed. When seed is set (via any of the three sources), the wire is encrypted; when None, the wire is unencrypted (full rc2 back-compat). The server auto-writes the resolved seed to the discovery file so subsequent clients on the same machine can find it (suppressible via_seed.resolve_server_seed(..., write_discovery_file=False)). - Added: per-direction
ChainStatediscipline — each connectedChannel(client) and each accepted worker (server) carries TWO independent chain states (send + recv), keyed with direction tags"out"/"in"so the two halves of the duplex never share a keystream. Concurrent client connections each get their own chain pair derived at accept time — no shared mutable state across simultaneous clients. - Added: per-frame envelope on the wire body —
[16-byte mac][8-byte counter_be][ciphertext]. Tampered frame raisesMacMismatchError(constant-timehmac.compare_digestverification); replayed or reordered frame raisesCounterReplayError. Short body raisesChainFormatError. - Added:
Channel.encrypted/Endpoint.encryptedproperties — query whether a particular bus surface is running the cipher. - Tests: ~30 new tests under
tests/test_bus.pycovering chain encrypt/decrypt round-trip, MAC mismatch detection, counter replay rejection, seed-mismatch behaviour at the channel layer, unencrypted back-compat preserved, tool-schemadecode_spliceintrospection, end-to-end viaserve()+connect()with seed, seed-file priority cascade, direction-tag keystream disjointness.
Threat model: defensive against same-user processes that didn't initiate the channel. NOT designed for active local attackers (would need DH key establishment + AEAD; deferred to v0.5.x or v0.6.0 if needed). Honest scope: a co-resident process that can read the discovery file at rest (no kernel-isolation; 0o600 is only file-perm-level) can decrypt; the defence is structural ("you weren't part of the chain since state_0").
[0.5.0rc2] - 2026-05-28¶
rc2 of N for v0.5.0 — C peer + real Windows named pipe + envelope fixes.
Per user direction 2026-05-28 (continuation of the rc1 dispatch): three
changes folded into one rc — the bus C peer for sub-µs native dispatch
(when both ends opt in), real Windows named pipes via Win32 ctypes
(no pywin32 dependency), and two envelope bugs from the rc1 cross-
process smoke that silently dropped handler-returned keys and
over-restricted client payload schema.
- Added:
srmech_bus_*C symbols —srmech_bus_serve,srmech_bus_server_accept_one,srmech_bus_server_stop,srmech_bus_connect,srmech_bus_send_recv,srmech_bus_client_close— plus the function-pointer typedefsrmech_bus_handler_callback_t. JPL-clean POSIX (AF_UNIX) + Windows (CreateNamedPipe via Win32, no pywin32 dep). Workspace allocated once per server atsrmech_bus_serve; reused across every accepted connection (no allocation in the hot path; JPL Rule 3 honored via cold-path-only allocation allowance). ABI bump 2 → 3 (the new function-pointer typedef carries a wire- format implication for the Python ctypes CFUNCTYPE construction). - Added: Python ctypes binding
srmech.amsc._native.BUS_HANDLER_CALLBACK - bindings for all six new C symbols (hasattr-guarded so a stale rc1 lib falls through cleanly to the Python-only path).
- Added:
srmech.bus._transport.NamedPipeTransport+ the_SafeNamedPipeServerTransportwrapper that auto-falls-back to TCP-loopback onCreateNamedPipeWfailure (rare; for sandboxed test environments). Default Windows transport remains TCP-loopback for rc2; opt-in to the named-pipe path viaSRMECH_BUS_USE_NAMED_PIPE=1. (The named-pipe accept loop on Windows 10 / Python 3.14 exhibited a Connect/accept-ordering regression undermultiprocessing.spawn— the firstConnectNamedPipecompleted without a corresponding clientCreateFileW, leaving the worker reading from a phantom connection; the rc2 commit message documents the investigation; not yet root-caused. The C peer + Python ctypes infrastructure are in place for a later rcN to flip the default once the Connect/accept race is understood.) - Added: discovery registry token now accepts
pipe \\.\pipe\srmech-{name}(rc2 named-pipe servers) in addition totcp 127.0.0.1 <port>(rc1 fallback / locked-down environments)._endpoint_alive_named_pipeprobes viaWaitNamedPipeW(which does not consume a pipe instance per Microsoft docs). - Fixed: handler return-shape now correctly passes the full handler
dict through as the response Event's
payload(rc1 was silently aliasingpayloadtohandler_result.get("payload")and dropping every other key). A handler returning{"type": "pong", "echo": ..., "server_pid": ...}now delivers all three keys to the client end-to-end. Thetypediscriminator is also retained inside the payload for client-side inspection. Handler contract documented in_server.py:_normalise_response. - Fixed: client
Channel.send()no longer requirespayloadto be a dict. Any JSON-serialisable value is accepted (string, list, number, bool,None, dict).json.dumpsraises at the canonical serialisation boundary on truly non-serialisable inputs. Matches standard JSON conventions; supports e.g.{"type": "ping", "payload": "echo-me"}and{"type": "metrics", "payload": [1, 2, 3]}. - Fixed: handlers receive
payloadof the original JSON type (string / list / number / dict / None), not coerced. Server's_event_to_dictno longer wraps non-dict payloads as{"value": ...}. - Changed: handlers without a
typekey in their return dict now default to discriminator"ok"(rc1 defaulted to"_response"; the rc2 default matches the spec's "type=ok default" idiom). - Changed: handler exceptions yield
{"type": "_error", "reason": ..., "traceback": ...}(rc1 nested these insidepayloadwhich the Bug-1 fix obviated; the rc2 error envelope is flat and the full dict still passes through as the response payload per the new contract). - Tests: ~13 new tests covering Bug-1 (handler full-dict pass-through), Bug-2 (any-JSON-payload), ABI v3 verification, native C-peer symbol presence, BUS_HANDLER_CALLBACK CFUNCTYPE constructibility. Total bus test count 49 → 62; full suite 1453 → 1457 passing.
- JPL audit:
srmech_bus.copted intoRULE_3_COLD_PATH_FILES(cold-path-only allocation; no malloc in accept loop or per- request worker). Seven small bus low-level helpers added toRULE_5_EXEMPT_FUNCTIONSper the established static-internal- trivial-wrapper pattern. Rule 4 / Rule 8 / Rule 1 all clean without exemption.
Remaining v0.5.0 rcs: rc3 state-chained wire format (per user
direction 2026-05-28 — TOTP-like rolling cipher with srmech-provided
decode_splice via tool-schema; same-user-defensive forward
secrecy), rc4 CLI, rc5 async wrapper, rc6 MCP adapter (Claude Code
integration), rc7 DSL runner, rc8 optional Anthropic SDK adapter.
[0.5.0rc1] - 2026-05-28¶
rc1 of N for v0.5.0 — srmech.bus Python skeleton.
Per user direction 2026-05-28: a cross-process bus over Unix-domain-sockets (POSIX) and Windows-named-pipes / TCP-loopback fallback (Windows), TLV-framed, MPR-NDJSON payloads, bidirectional req/rep + pub/sub. End-goal: srmech/siona processes (and Claude Code via MCP, rc5) compose across process boundaries.
Framework reading: Class M ∘ Class B ∘ Class A extended to the OS-process-class boundary. Class H introspection (v0.4.6) is the unidirectional read-only special case of the bus.
- Added:
srmech.busmodule —serve(),connect(),list(),pipe(),Endpoint,Channel,Event. Sync API. Pure Python (no C peer yet — that's rc2; ABI unchanged at 2). - Transport: POSIX Unix domain sockets at
~/.srmech/bus-{name}.sock(permissions0o600) + Windows TCP-loopback fallback with a registry file at~/.srmech/bus-{name}.txtrecording the kernel-assigned port. Real named-pipes via ctypes is the rc2 target; the rc1 fallback keeps the wire protocol identical so the rc2 swap is transport-only. - Framing: 4-byte length-prefix TLV; payload is JSON-encoded MPR-shaped
Event(mpr_version + type + payload + attestation + correlation_id). - Discovery:
~/.srmech/bus-{name}.sock(POSIX) /~/.srmech/bus-{name}.txtregistry (Windows). Same ownership-filter as introspect — notop/ps/Get-Processneeded. - Req/rep: client
send()issues a fresh UUID correlation-id and blocks for the matching reply; server handler returns a dict (response) orNone(fire-and-forget). - Pub/sub: server
Endpoint.broadcast()fans out to every connected subscriber; clientChannel.subscribe()yields broadcast events. Drop-newest / drop-oldest backpressure policies (server / client). - Daemon pipe:
pipe(source, sink, transform=...)composes two endpoints. - Off by default; importing
srmech.busbinds nothing. - ~30-40 new parity tests; full suite passes.
Remaining v0.5.0 rcs queued: rc2 (C peer + ctypes Windows-named-pipe), rc3 (CLI), rc4 (async wrapper), rc5 (MCP server adapter for Claude Code), rc6 (DSL runner consuming bus), rc7 (optional Anthropic SDK adapter).
[0.4.6] - 2026-05-28¶
Clean ship — two-arc v0.4.6 closed (PyPI description SO(8) refresh + out-of-band introspection).
PyPI metadata refresh leads with substrate-native 28-dim chiral hyper-loop = 𝔰𝔬(8) adjoint framing (per user 2026-05-28 "strip MVP false things"). NEW public surface: srmech is now installable as a console-script binary — pip install srmech puts srmech on PATH for the first time, with one subcommand srmech status providing out-of-band introspection of running srmech sweeps.
rc1 (TestPyPI verified): PyPI description field rewritten. 502 chars; both pyproject + pyproject-pure identical. Leads with 28D = 𝔰𝔬(8) adjoint framing.
rc2 (TestPyPI verified incl. CLI on PATH + live publish/status/auto-cleanup):
- Added: srmech.introspect module — publish() context manager; list() enumerates active runs (filters by file ownership; no top/ps/Get-Process needed); by_pid(N).follow() streams events; frozen Run + Event dataclasses (MPR-shaped; attestable).
- Added: srmech status [--pid N] [-f] CLI subcommand + pip install srmech → srmech console-script on PATH (NEW public surface).
- Added: SRMECH_PUBLISH_STATUS=1 env var auto-activates publish process-wide.
- File backend: ~/.srmech/run-{pid}-{start_time_ns}.ndjson (start_time_ns defeats PID recycling).
- Off by default; off-path emit check ≈174ns; on-path emit ≈76µs (json+file write+flush).
- Cross-platform Linux/macOS/Windows (POSIX os.kill(pid, 0); Windows ctypes OpenProcess — no pywin32 dep). Pyodide degrades cleanly.
- 35 new tests in test_introspect.py; full suite 1392 passing.
Framework reading: introspection IS Class H (self-introspection) extended across the OS-process boundary; the running process IS the spatial manifold, the introspection API IS its algebraic projection. The "srmech calls itself" extension composes with Spike #219 / MFO §VII.6.11 substrate-self-recognition cascade.
ABI: unchanged at 2 (no C changes; pure Python module).
[0.4.6rc2] - 2026-05-28¶
Out-of-band introspection — talk-to-running-PID API. Per user
direction 2026-05-28: srmech now exposes its internal current state
over a file-based API. Long-running sweeps (30 min to hours) become
observable from a second process without monkey-patching, GDB attach,
or top / ps polling. Substrate-self-recognition extended across
the OS-process boundary (the framework reading: Class H at PID level).
- Added:
srmech.introspectmodule —publish()context manager;list()enumerates active runs (filters by file ownership; notopneeded);by_pid(N).follow()streams events;Run+Eventfrozen dataclasses. - Added:
srmech status [--pid N] [-f]CLI subcommand. Also wiredpython -m srmech status ...viasrmech/__main__.pyand the[project.scripts]console-scriptsrmech = "srmech.cli:main"in both pyprojects. - Added:
SRMECH_PUBLISH_STATUS=1env var auto-activates publish. - File backend:
~/.srmech/run-{pid}-{start_time_ns}.ndjson(start_time_ns defeats PID recycling). MPR-shaped events (re-uses srmech.amsc.format envelope — introspection is itself attestable). - Off by default; zero cost when not used. Emit hooks at cascade-op
(all 8 ops in
srmech.amsc.cascade) + AMSC-fetch (adapters._base.run) - signal-processing (
cascade_dispatcher.dispatch+ RBS-HDC encode/decode/similarity boundaries) are no-ops without publish. - Auto-cleanup:
list()checksos.kill(pid, 0)(POSIX) / Win32OpenProcess(Windows); removes orphan files; reports dead PIDs as "died" with the last event's data preserved in the returned Run. - ~30 new parity tests (
tests/test_introspect.py). Tier 1 (status-file) ships; Tier 2 (mmap ring buffer for >1k events/sec) deferred until an op proves it needs it. - Cross-platform: Linux/macOS/Windows. Pyodide degrades cleanly.
ABI unchanged at 2 (no C changes; pure Python module).
[0.4.6rc1] - 2026-05-28¶
PyPI metadata refresh — leads with SO(8) 28D framing. Description-only change; no code, no test, no ABI delta. Reason: the prior v0.4.5 PyPI description listed the 14-class primitive vocabulary without ever mentioning that the substrate the vocabulary instantiates is the 28-dim chiral hyper-loop = 𝔰𝔬(8) adjoint (14 𝔤₂ derivations + 14 L⊕R octonion-multiplications; Spin(8) triality) — out of step with the docs/RTD MFO/substrate-native blocks updated in PR #698 + the v0.4.5 cascade-catalog C-parity arc that made the 28D substrate hardware- callable. Per user 2026-05-28 ("strip MVP false things"), refreshed.
- Changed:
pyproject.toml+pyproject-pure.tomldescriptionfield — leads with "substrate-native 28-dim chiral hyper-loop = so(8) adjoint (14 g_2 derivations + 14 L+R octonion-multiplications; Spin(8) triality) made hardware-callable"; keeps the 14-class vocabulary enumeration for PyPI search; mentions cascade-catalog C/Python parity (v0.4.5 arc). 502 chars, both files identical. - Version: 0.4.5 → 0.4.6rc1 across 5 SSOTs + version-pin test rename.
rc1 → TestPyPI; clean v0.4.6 follows after verify on the project page.
[0.4.5] - 2026-05-28¶
Cascade-catalog C/Python parity + TOML retrofit — ARC CLOSED.
Clean ship after rc1-rc8 sequence. All 8 cascade catalog ops now have
full C/Python parity (10 C symbol families total) plus declarative TOML
descriptors under srmech/amsc/_research/cascade_catalog/. Corrects the
v0.4.3rc6 + v0.4.4rc1 carve-out that shipped cascade ops Python-only.
Cascade C peers added (one per rc):
- rc1 srmech_cascade_chiral_flip_i64 + _f64 — Class C orientation reversal (sequence in/out; in-place safe).
- rc2 srmech_cascade_pin_slot_at_zero_f64 — Class K pin-slot (scalar in / orientation+magnitude out via output pointers; NaN→dead-band).
- rc3 srmech_cascade_magnitude_f64 — Class K magnitude-only (scalar in/out; explicit 3-branch impl preserving NaN→0.0 parity).
- rc4 srmech_cascade_reorient_i64 + _f64 — Class C re-application (two-arg shape; type-preserving; INT64_MIN guarded).
- rc5 srmech_cascade_net_chirality_i8 — Class C net handedness (sequence in / scalar out; empty→+1; first zero short-circuits to 0).
- rc6 srmech_cascade_cyclic_gcd_u64 — Class I cascade-namespace wrapper (delegates to existing srmech_gcd primitive).
- rc7 srmech_cascade_best_rational_signed_f64 — multi-class K∘N∘C cascade (delegates Class N to srmech_best_rational; banker's rounding via llrint() for Python parity).
- rc8 srmech_cascade_chiral_dual_f64 — HIGHER-ORDER (callback ABI via srmech_cascade_op_callback_f64_t typedef; caller-allocated workspace per JPL Rule 3; delegates inner+outer chiral_flip to rc1 native peer).
Added:
- 8 TOML cascade-catalog entries under srmech/amsc/_research/cascade_catalog/ documenting each cascade's class composition, native symbol, attestation, and (where applicable) [cascade.delegates_to] / [cascade.composes] / [cascade.higher_order] / [cascade.callback_marshaling] / [cascade.rounding] / [cascade.boundary_cases] sections.
- New public C typedef srmech_cascade_op_callback_f64_t for higher-order callback ABI.
- ~150 new parity tests across all 8 ops (covering int / float / numpy / NaN / Inf / dead-band / banker's-rounding boundary / callback exception propagation / etc).
Changed:
- srmech.amsc.cascade module docstring: removed "no dedicated C symbol" carve-out clause; added "Full C/Python parity" discipline statement.
- README.md cascade-catalog section: stripped "no dedicated C symbol" carve-out per user directive 2026-05-28 ("strip MVP false things from our presence"); added "Each cascade ships with a dedicated C symbol in libsrmech (full C/Python parity per project discipline)" statement; per-op annotations updated with C-peer rc references.
- All 8 cascade Python entry points now dispatch through native when input shape matches the typed C variant; Python fallback retained for shapes the C ABI doesn't cover (strings, mixed types, out-of-int64 bigints, generators, etc).
Discipline preserved:
- ABI unchanged at 2 throughout (all rcs were additive symbols + one additive typedef).
- JPL Power-of-Ten 6/6 audit clean across the entire arc (≥2 asserts per non-exempt function, ≤60-line functions, no malloc inside libsrmech, no goto, bounded loops).
- No abs() in cascade Python (sign-handling via canonical Class K pin-slot + Class C re-orientation cascade).
- No new hashlib.sha256(...) direct calls (route through format.sha256_bytes).
- 1360 test suite all passing.
[0.4.5rc8] - 2026-05-28¶
Cascade-catalog C/Python parity + TOML retrofit — chiral_dual (rc8 of 8; HIGHER-ORDER callback ABI; CLOSES THE ARC). After this ship all 8 cascade catalog ops have full C/Python parity + TOML descriptors. The carve-out corrections begun in v0.4.5rc1 are complete.
chiral_dual is the ONLY higher-order cascade op in the catalog — it
takes a callable op as input and conjugates it with Class C
orientation reversal: chiral_flip(op(chiral_flip(x))). The C peer
uses a function-pointer callback ABI (Option A) rather than a
Class-ID enum dispatch (Option B); Option B would have restricted
chiral_dual to known A-N srmech ops, breaking the cascade-catalog
public API contract that op can be any callable.
- Added:
srmech_cascade_chiral_dual_f64C symbol (higher-order; callback ABI viasrmech_cascade_op_callback_f64_ttypedef; caller-allocated workspace per JPL Rule 3; delegates the Class C inner+outer chiral_flip to the rc1 native peer). ABI unchanged at 2. - Added:
srmech_cascade_op_callback_f64_tpublic typedef insrmech.h(callback signature for higher-order cascade ops). - Added:
_research/cascade_catalog/chiral_dual.toml— eighth and final TOML cascade-catalog entry with[cascade.higher_order]+[cascade.callback_marshaling]+[cascade.design_choice]sections documenting the callback ABI + the Option A vs Option B design decision. - Added:
tests/test_cascade_chiral_dual_parity.py— parity across identity / negation / non-trivial / ndarray / empty / singleton / random sweep / Python exception propagation / wrong-length-output guard / mixed-type fallback / string fallback / non-callable op fallback. - Added:
CASCADE_OP_CALLBACK_F64ctypes CFUNCTYPE exposed atsrmech.amsc._nativemodule scope (mirrors the C typedefsrmech_cascade_op_callback_f64_t) so the Python dispatch can construct callback instances without reaching into the library- binding closure. - Changed:
srmech.amsc.cascade.chiral_dualdispatches through native for homogeneous float64 sequences (list / tuple / 1-D ndarray); Python fallback retained for strings, mixed-type sequences, non- callable ops, multi-arg ops, etc. Python exceptions raised by the op callback propagate correctly through the trampoline (never silently swallowed).
Cascade-catalog C-parity + TOML retrofit arc CLOSED at rc8. All 10 cascade C symbol families exported (chiral_flip i64+f64, pin_slot_at_zero f64, magnitude f64, reorient i64+f64, net_chirality i8, cyclic_gcd u64, best_rational_signed f64, chiral_dual f64). Ready for clean v0.4.5 ship to production PyPI.
[0.4.5rc7] - 2026-05-28¶
Cascade-catalog C/Python parity + TOML retrofit — best_rational_signed (rc7 of N; multi-class K∘N∘C cascade with delegation to existing Class N primitive). PLUS: README full-feature update strips residual "no dedicated C symbol" carve-out language per user directive 2026-05-28 ("strip MVP false things from our presence").
best_rational_signed is the SECOND of the delegating cascade ops in
this arc (after cyclic_gcd / rc6). The C peer composes three A–N stages:
Class K pin-slot (sign-strip) inlined + Class N best-rational anchor
delegated to the existing srmech_best_rational primitive + Class C
re-orientation (sign re-apply on the numerator) inlined. The multi-
stage delegation pattern generalises rc6's single-class delegation
pattern to the multi-stage case.
Banker's-rounding parity (load-bearing): Python's built-in round() uses
round-half-to-even (banker's rounding); C99 round() uses round-half-
AWAY-from-zero. The C peer uses llrint() under the default IEEE-754
FE_TONEAREST mode (= round-half-to-even) for bit-exact parity with
Python's round() at the .5 boundary.
- Added:
srmech_cascade_best_rational_signed_f64C symbol (JPL-clean; multi-stage K∘N∘C cascade; Class K + Class C stages inlined; Class N delegated to existingsrmech_best_rationalprimitive; banker's rounding viallrint()for Python parity). ABI unchanged at 2 (additive symbol). - Added:
srmech/amsc/_research/cascade_catalog/best_rational_signed.toml— seventh TOML cascade-catalog entry with[cascade.composes]/[cascade.delegates_to]/[cascade.rounding]sections documenting the multi-stage composition + the IEEE-754 rounding-mode choice. - Added:
tests/test_cascade_best_rational_signed_parity.py— parity across basic positives / basic negatives / origin / sub-dead-band / NaN / tiny / large / custom kwargs / invalid kwargs / random sweep / banker's-rounding boundary (load-bearing — confirms llrint() vs C99 round() distinction holds at the .5 boundary). - Changed:
srmech.amsc.cascade.best_rational_signeddispatches through native for pure-Pythonfloatx+ Pythonintkwargs (not bool) in int64 range; Python fallback retained for numpy scalars, Decimal, larger-than-int64 kwargs, and any other shape the strict native ABI doesn't cover. The pre-rc7 ValueError-on-invalid-kwargs public API is preserved exactly (native path skips on invalid kwargs and falls through to the Python path which raises with the proper message). - Changed: README cascade-catalog section corrected — removed "no dedicated C symbol" carve-out; added "Each cascade ships with a dedicated C symbol in libsrmech (full C/Python parity)" discipline statement; per-op annotations updated with C-peer rc references (rc1-rc7; rc8 chiral_dual queued).
Remaining: rc8 chiral_dual (higher-order; callback ABI design — closes the arc). After rc8: clean v0.4.5 ship to production PyPI.
[0.4.5rc6] - 2026-05-28¶
Cascade-catalog C/Python parity + TOML retrofit — cyclic_gcd (rc6 of N; FIRST of the delegating cascade ops).
cyclic_gcd is a pure-delegation cascade — the cascade-catalog entry IS
the Class I primitive (Euclid gcd; srmech_gcd). Per the user's
"delegate to A-N C peers; cascade-level C wrapper + TOML" directive,
this rc ships a thin cascade-namespace wrapper that internally calls
the existing Class I C primitive, plus a TOML descriptor with a
[cascade.delegates_to] section documenting the delegation.
- Added:
srmech_cascade_cyclic_gcd_u64C symbol — cascade-namespace wrapper that delegates to the existing Class I primitivesrmech_gcd. uint64 inputs / uint64 output via pointer, mirroring the Class I primitive's signature exactly. ABI unchanged at 2 (additive symbol). - Added:
srmech/amsc/_research/cascade_catalog/cyclic_gcd.toml— sixth TOML cascade-catalog entry with[cascade.delegates_to]documenting the Class I primitive linkage (cascade-as-named-pattern vs primitive-class operation). - Changed:
srmech.amsc.cascade.cyclic_gcddispatches through the cascade-namespace wrapper for(int, int)inputs in the uint64 range; Python fallback (which itself routes to the Class I primitive viasrmech.amsc.cyclic.gcd) covers bool, negative, and out-of-uint64 bigint inputs. The public API is unchanged: negative inputs and bigints still raiseValueErrorvia the Python ref.
Remaining 2 cascade ops queued: best_rational_signed (multi-class K∘N∘C cascade), chiral_dual (higher-order; callback ABI design).
[0.4.5rc5] - 2026-05-28¶
Cascade-catalog C/Python parity + TOML retrofit — net_chirality (rc5 of N; LAST of the simple pure-Python cascade ops in this arc).
- Added:
srmech_cascade_net_chirality_i8C symbol (JPL-clean; sequence in / scalar out via output pointer; empty input → +1; zero-element short-circuits to 0; bounded loop). ABI unchanged at 2. - Added:
srmech/amsc/_research/cascade_catalog/net_chirality.toml— fifth TOML cascade-catalog entry with boundary-cases section. - Changed:
srmech.amsc.cascade.net_chiralitydispatches through native for list[int] / tuple[int] / 1-D int ndarrays where every element fits int8; Python fallback covers generators, bool elements (False == 0 short-circuits via Python iteration), out-of-int8 values, mixed types.
Remaining 3 cascade ops queued: cyclic_gcd (delegates to existing Class I C peer), best_rational_signed (multi-class K∘N∘C cascade), chiral_dual (higher-order; callback ABI design).
[0.4.5rc4] - 2026-05-28¶
Cascade-catalog C/Python parity + TOML retrofit — reorient (rc4 of N; continues the carve-out correction started in rc1).
- Added:
srmech_cascade_reorient_i64+srmech_cascade_reorient_f64C symbols (JPL-clean; two-arg shape: int8 orientation x scalar value; type-preserving int/float dispatch; IEEE-754 negation semantics for f64). ABI unchanged at 2 (additive symbols). - Added:
srmech/amsc/_research/cascade_catalog/reorient.toml— fourth TOML cascade-catalog entry with native-symbol mapping + INT64_MIN boundary-case documentation + attestation. - Changed:
srmech.amsc.cascade.reorientnow dispatches through native forint8 orientation x int64 valueorint8 orientation x float64 value; Python fallback retained for numpy scalars, ndarrays, lists, mixed types, bool orientation, out-of-int64 values, and INT64_MIN guard (avoids overflow). - Added:
tests/test_cascade_reorient_parity.py— parity across int / float / numpy / list / NaN / ±Inf / INT64_MIN guard / out-of-int64 fallback / bool orientation / out-of-int8 orientation / random sweeps.
Remaining 4 cascade ops queued: net_chirality, cyclic_gcd, best_rational_signed, chiral_dual.
[0.4.5rc3] - 2026-05-28¶
Cascade-catalog C/Python parity + TOML retrofit — magnitude (rc3 of N; continues the carve-out correction started in rc1).
- Added:
srmech_cascade_magnitude_f64C symbol (JPL-clean; scalar f64 in / out via output pointer; NaN maps to dead-band 0.0 matching Python ref). ABI unchanged at 2 (additive symbol). - Added:
srmech/amsc/_research/cascade_catalog/magnitude.toml— third TOML cascade-catalog entry with native-symbol mapping + attestation + explicit composes-from-pin_slot_at_zero declaration. - Changed:
srmech.amsc.cascade.magnitudenow dispatches through native for pure-Pythonfloatinputs; Python fallback composespin_slot_at_zero(x)[1](which itself dispatches native for floats via rc2) forint/ numpy-scalar / other numeric types. - Added:
tests/test_cascade_magnitude_parity.py— parity across int / float / 0.0 / -0.0 / NaN / Inf / small / large / bool / random sweep + composition equivalence with pin_slot_at_zero.
Remaining 5 cascade ops queued: reorient, net_chirality, cyclic_gcd, best_rational_signed, chiral_dual.
[0.4.5rc2] - 2026-05-28¶
Cascade-catalog C/Python parity + TOML retrofit — pin_slot_at_zero (rc2 of N; continues the carve-out correction started in rc1).
- Added:
srmech_cascade_pin_slot_at_zero_f64C symbol (JPL clean; scalar in / (int8 + double) out via output pointers; NaN maps to the dead-band matching Python's reference behaviour). ABI unchanged at 2 (additive symbol). - Added:
srmech/amsc/_research/cascade_catalog/pin_slot_at_zero.toml— second TOML cascade-catalog entry with native-symbol mapping + attestation. - Changed:
srmech.amsc.cascade.pin_slot_at_zeronow dispatches through native forfloatinputs; Python fallback retained forintand other numeric types (preserves the int-in / int-magnitude- out type contract). - Added:
tests/test_cascade_pin_slot_at_zero_parity.py— parity tests across int / float / 0.0 / -0.0 / NaN / Inf / small / large.
Remaining 6 cascade ops queued: magnitude, reorient, net_chirality, cyclic_gcd, best_rational_signed, chiral_dual.
[0.4.5rc1] - 2026-05-28¶
Cascade-catalog C/Python parity + TOML retrofit — chiral_flip (carve-out correction). The v0.4.3rc6 + v0.4.4rc1 cascade-catalog ships codified a carve-out from the project's full-C-parity discipline by shipping cascade ops as Python-only compositions with no C symbols and no TOML descriptors. This rc begins the correction by retrofitting chiral_flip with both. The remaining seven cascade ops will follow in subsequent rcs.
- Added:
srmech_cascade_chiral_flip_i64+srmech_cascade_chiral_flip_f64native C symbols (JPL Power-of-Ten clean; in-place safe; ≥2 asserts; bounded loops; no malloc). ABI unchanged at 2 (additive symbol, not a wire-format change). - Added:
srmech/amsc/_research/cascade_catalog/chiral_flip.toml— first TOML cascade-catalog entry with native-symbol mapping + attestation. - Changed:
srmech.amsc.cascade.chiral_flipnow dispatches through native when input islist[int]/list[float]/ndarray[int64|float64]; Python fallback retained for string / tuple / mixed-type inputs. - Changed: module docstring corrected — removed the "no dedicated C symbol" carve-out sentence; added "full C/Python parity" discipline statement.
- Added:
tests/test_cascade_chiral_flip_parity.py— C/Python parity tests for int64, float64, list, tuple, ndarray, empty, singleton, odd-length.
Remaining 7 cascade ops queued for subsequent rcs in this v0.4.5 line.
[0.4.4] - 2026-05-28¶
Production release → PyPI. Consolidates rc1 (cascade chirality mini-set) + rc2 (bundled siona co-name alias), each shipped + clean-venv-verified on TestPyPI first. No new primitive class anywhere; ABI unchanged at 2; no new C symbol.
- Cascade chirality mini-set (
srmech.amsc.cascade, rc1) —chiral_flip(Class C orientation reversal),chiral_dual(Class C ∘ op ∘ Class C: same spectral shape, inverted orientation — verified across all 14 A–N operators),net_chirality(conserved Class-C cascade invariant). Tool-schema entries +tests/test_cascade_chirality.py. - Bundled
sionaco-name alias (rc2) — the srmech wheel ships a second top-level package,siona, sopip install srmechmakesimport sionaresolve to exactly the same objects asimport srmech(everysrmech.*submodule mirrored undersiona.*). srmech stays the single source of truth (native lib,__version__, tool-schema).tests/test_siona_alias.py(6 tests). Pairs with the standalonesionametapackage on PyPI (pip install siona→srmech>=0.4.4, which provides the bundled alias).
Per-rc detail in the entries below.
[0.4.4rc2] - 2026-05-28¶
Bundled siona co-name alias → TestPyPI (rc). The srmech wheel now ships a second top-level package, siona, alongside srmech: pip install srmech makes import siona resolve to exactly the same objects as import srmech (every srmech.* submodule mirrored under siona.* via sys.modules alias + parent-attribute binding). No forked logic — srmech stays the single source of truth (native lib, __version__, tool-schema). No new class; ABI unchanged at 2.
siona/__init__.pyadded;wheel.packages/ hatchlingpackages→["srmech", "siona"];siona/**in both sdist includes.tests/test_siona_alias.py(6 tests): version match, top-level re-export, submodule identity, from-import, attribute-chain, callable-through-alias.- Pairs with the standalone
sionaPyPI distribution (a metapackage that depends on srmech and re-uses this same alias).
[0.4.4rc1] - 2026-05-27¶
Cascade chirality mini-set → TestPyPI (rc). Three callables added to the foundational srmech.amsc.cascade catalog. No new primitive class — each is a composition of the existing Class C orientation + Class K sign; ABI unchanged at 2; no new C symbol.
chiral_flip(seq)— Class C orientation reversal (seq[::-1]); the value-level chirality operator.chiral_dual(op, x)— Class C ∘ op ∘ Class C: run an operator in the opposite Class-C orientation. The chiral dual of an A–N operator is same spectral shape, inverted orientation (magnitude preserved, phase flipped) — verified across all 14 operators (MFO §VIII.31.11 §(5b)/(5c); committed spikedocs/srmech/notes/spike_chiral_an_spectral_shape.py). Reduces to the bare Class K−1for the sign operators (C, N); identity for real-symmetric (L).net_chirality(orientations)— Class C net handedness of a cascade (product of per-op orientations via composedreorient;0if any is neutral) — the conserved Class-C invariant a chiral cascade reads out.- Tool-schema entries +
tests/test_cascade_chirality.pyadded;CASCADE_OPSand__all__extended.
[0.4.3] - 2026-05-27¶
Production release of the "Class M variant expansion" arc → PyPI. Consolidates rc1–rc6 (each shipped + clean-venv-verified on TestPyPI first). No new primitive class anywhere — every addition is a variant or composition of the existing 14-class A–N vocabulary; ABI unchanged at 2.
- rc1 —
polar{-1,0,+1}HDC variant (Class M∘K; absorbing-zero dead-band) + C parity. - rc2 —
Klein-4(ℤ₂)²HDC variant (rank-2 abelian; quad-DNA / two-axis chirality) + C parity. - rc3 —
srmech.amsc.coupling.signed_sum_squared(Class K∘L signed-sum coupling score). - rc4 —
srmech.amsc.laplacian.symmetric_eigendecompose(real-symmetric Class L; real float64 eigvecs). - rc5 —
rfftreal-input half-spectrum dual-path signal-processing op (Class A∘I∘K). - rc6 —
srmech.amsc.cascadefoundational cross-domain cascade catalog (pin_slot_at_zeroK /reorientC /magnitudeK /best_rational_signedK∘N∘C /cyclic_gcdI) — a named cascade is the default, a math-library call the exception.
Plus the PyPI README companion-textbook slot for the Technical Disclosure Commons defensive publication of The Metric Field and Its Primitives (Kirkland, 2026-05-25). Per-rc detail in the entries below.
[0.4.3rc6] - 2026-05-27¶
rc6 of the v0.4.3 "Class M variant expansion" rolling arc — the foundational cross-domain cascade catalog srmech.amsc.cascade. The cascades that recur across every / most domains the framework has examined, promoted into srmech so a named cascade is the default and a math-library call is the exception. Per the project discipline: being forced to reach for a math library is the signal that a cascade is waiting to be found — abs() told us to find the Class-K pin-slot, fractions the Class-N rational anchor, math.gcd the Class-I cyclic gcd. No new primitive class — every op is a composition of the existing 14-class A–N primitives, so no dedicated C symbol.
Added — srmech.amsc.cascade¶
Graduates the precursor docs/unsolved-maths/_cascade_helpers.py (imported across 20+ cascade scripts spanning mandelbrot / chromatic / atomic / nuclear / QCD / planetary / turbulence / black-hole / biomacromolecule / large-scale-structure domains) into srmech, justified by the framework's scale-invariance canon (the A–N operators are substrate-universal at every discipline and scale):
pin_slot_at_zero(x) -> (orientation, magnitude)— Class K pin-slot at zero; sign-flip IS the canonical phase-boundary. The cascade-honest split that replaces a bareabs().reorient(orientation, value)— Class C cascade-orientation re-apply.magnitude(x)— Class K magnitude-only convenience (theabs()replacement).best_rational_signed(x, *, max_denominator=100, fine_scale=1_000_000)— Class K ∘ N ∘ C: float → signed small-denominator rational (sign in the numerator, denominator positive; viasrmech.amsc.rational.best_rational). Noabs(); sign lives in the Class K / Class C pair.cyclic_gcd(a, b)— Class I (delegates tosrmech.amsc.cyclic.gcd); the cascade-named alias formath.gcd.
Back-compat aliases (class_k_pin_slot_at_zero, class_c_reorient, best_rat_signed) let the precursor's call sites migrate with a pure import swap. CASCADE_OPS registry + DEFAULT_MAX_DENOMINATOR / DEFAULT_FINE_SCALE constants exported. Per [[feedback_sign_handling_is_class_k_pin_slot_not_alu_abs]] — no abs() anywhere (AST-verified in tests).
Added — tests + tool_schema + README¶
tests/test_cascade_foundational.py — 35 tests: Class K orientation/magnitude split, magnitude == |x|, Class C reorient + round-trip, best_rational_signed known values + π anchor + sign-in-numerator + bounded denominator + validation, cyclic_gcd == math.gcd, back-compat alias identity, registry/__all__, and an AST check that the module never calls abs(). 5 tool_schema ToolEntry registrations (category cascade). README gains a srmech.amsc.cascade composition-layer subsection + status-banner update. Version-pin asserts bumped 0.4.3rc5 → 0.4.3rc6.
[0.4.3rc5] - 2026-05-27¶
rc5 of the v0.4.3 "Class M variant expansion" rolling arc — the rfft real-input half-spectrum signal-processing op, per UPSTREAM_NOTES §1.1 (RBS-LM research subtree, surfaced by R-RBS-LM-49z). A dual-path op (Path A reference + Path B native), composing the same Class A∘I∘K cyclic-DFT algebra as fft; no new primitive class — the 14-class A–N vocabulary is intact per [[feedback_no_privileged_primitive_classes]].
Added — srmech.signal_processing.{closed_form_ops,path_b_ops}.rfft¶
Real-input forward FFT returning only the non-redundant first N//2 + 1 bins. For a real signal the full DFT is Hermitian-symmetric (X[N−k] = conj(X[k])), so the second half carries no new information — half the compute and half the memory of fft, algebra-identical on the retained bins. The use case (UPSTREAM_NOTES §1.1): real bipolar bit-string FFT cascades ({−1, +1}) that previously used the full fft at 2× cost.
Identity (Spike #176 H1, machine ε): the cyclic-DFT IS Class A (content-address on sequence order) ∘ Class I (cyclic-group ℤ/N) ∘ Class K (rotation as pin-slot on the unit circle); rfft is that composition on the real-symmetric half-substrate — the Hermitian conjugate-symmetry IS the reflection the Class K pin-slot already encodes.
- Path A
closed_form_ops.rfft.op—numpy.fft.rfftreference. - Path B
path_b_ops.rfft.op— Class K cycle-order verification (Spike #176 T8) then cyclic-substraterfft; D1 algebra-identical to Path A. Both paths registered withpath_registry.
Roster note: like
pi_cascade,rfftis a post-Phase-4 addition — it is in the package__init__imports +__all__but NOT in the frozenPATH_B_MVP_OPS(still 6) orPATH_A_OP_MODULES(still 38) rosters.
Added — tests + README¶
tests/test_signal_processing_rfft.py — 20 tests: Path A↔numpy parity (incl. truncate/zero-pad n), Path B↔Path A D1 identity, half-spectrum identity (rfft == fft[:N//2+1]), Hermitian full-spectrum reconstruction, bipolar bit-string use case, both-paths registration + metadata, and the frozen-MVP-roster guard. README signal_processing Path A op list + dual-path line updated to surface rfft. Version-pin asserts bumped 0.4.3rc4 → 0.4.3rc5.
[0.4.3rc4] - 2026-05-27¶
rc4 of the v0.4.3 "Class M variant expansion" rolling arc — the symmetric_eigendecompose real-symmetric Class L op, per UPSTREAM_NOTES §2.1 (RBS-LM research subtree). A real-input specialisation of the existing hermitian_eigendecompose; no new primitive class — the 14-class A–N vocabulary is intact per [[feedback_no_privileged_primitive_classes]].
Added — srmech.amsc.laplacian.symmetric_eigendecompose¶
Real-symmetric eigendecomposition L = V · diag(eigvals) · Vᵀ via numpy.linalg.eigh. The Hermitian path returns a complex128 eigenvector matrix V, which raises a ComplexWarning when a caller already knows the input is real-symmetric (the common case — a graph Laplacian). This specialisation guarantees real float64 eigvals AND eigvecs. Class L (graph spectral / eigendecomposition). Canonical SSoT: Golub & Van Loan, Matrix Computations (4th ed.) §8.3.
Architecture note: no native C dispatch for this op. Eigenvector sign and degenerate-subspace rotation are non-unique, so element-wise C/Python parity is not a meaningful contract; correctness is instead pinned by eigenvalues + reconstruction (
V diag(w) Vᵀ ≈ L) + orthonormality (Vᵀ V ≈ I). Added toLAPLACIAN_OPS(composition-engine registry) and__all__.
Added — tests + tool_schema¶
tests/test_laplacian_class_l_broadening.py extended with 8 symmetric_eigendecompose tests (real-float64 dtype guarantee, numpy match + reconstruction, orthonormality, diagonal, connected-Laplacian nullspace ≈ 0, zero-size, non-square rejection, registry/__all__ membership). 1 tool_schema ToolEntry. Version-pin asserts bumped 0.4.3rc3 → 0.4.3rc4.
[0.4.3rc3] - 2026-05-27¶
rc3 of the v0.4.3 "Class M variant expansion" rolling arc — the signed_sum_squared coupling-score, per UPSTREAM_NOTES §1.2 (RBS-LM research subtree; R-RBS-LM-33 weak-coupling-truncate + R-RBS-LM-49 Method C). No new primitive class; this is a composition of existing Class K ∘ Class L primitives.
Added — srmech.amsc.coupling.signed_sum_squared¶
Per-element (Σ_sources (2·bit − 1))² across a stack of bit-arrays. The bipolar transform 2·bit−1 ∈ {−1,+1} is the Class-K sign-projection (no abs(), signed arithmetic only per [[feedback_sign_handling_is_class_k_pin_slot_not_alu_abs]]); the element-wise sum across sources then squared is the Class-L signed-magnitude-squared coupling score (sign-agnostic coupling strength, range [0, n_sources²]). Resolves the bare-numpy inline both R-RBS-LM partitions had used.
Architecture note: this is a Class K ∘ L composition operating on a stack — not a new primitive class, so it carries no dedicated C symbol: the underlying Class-K / Class-L primitives are the ones with C parity, and a composition sequences them in Python (the config-driven-vs-substrate-primitive split per CLAUDE.md). The 14-class A–N vocabulary is intact per
[[feedback_no_privileged_primitive_classes]].
Added — tests + tool_schema¶
tests/test_coupling_signed_sum_squared.py — 6 numpy-reference property tests (known values, full-agreement/balance, single-source, random reference-formula match, int64-nonnegative range, validation). 1 tool_schema ToolEntry. Version-pin asserts bumped 0.4.3rc2 → 0.4.3rc3.
[0.4.3rc2] - 2026-05-27¶
rc2 of the v0.4.3 "Class M variant expansion" rolling arc — the Klein-4 {0,1,2,3} HDC variant, per UPSTREAM_NOTES §4 (RBS-LM research subtree, Finding 132 / R-RBS-LM-97). Stacks on rc1 (polar). No new primitive class; Klein-4 is the rank-2 abelian Class M variant over (F₂)² = Z₂×Z₂ — the 14-class A–N vocabulary is intact per [[feedback_no_privileged_primitive_classes]].
Added — srmech.amsc.hdc Klein-4 {0,1,2,3} variant¶
The next rung of the Class-M variant ladder above polar (bipolar {-1,+1} → polar {-1,0,+1} → Klein-4 (Z₂)²). Each position is a 2-bit value (4 states), state = γ₅_bit·2 + iω₇_bit; the four states are the four chirality sectors of the MFO §VII.4.1.7 4-way (γ₅, iω₇) decomposition (visible/dark × matter/antimatter). This is the quaternary / DNA-like "quad" substrate carrying both chirality axes where bipolar/polar carry one. uint8 array representation.
klein4_random / klein4_bind(component-wise(F₂)²-XOR; commutative, associative, self-inverse, identity 0)/ klein4_unbind / klein4_bundle(per-bit majority, ties→0)/ klein4_similarity(match-fraction).klein4_chirality_flip_gamma5(XOR 2)/ klein4_chirality_flip_omega7(XOR 1)/ klein4_cpt_mirror(XOR 3)/ klein4_sector_count(per-sector occupancy attestation).
Added — C parity surface (srmech_klein4_{bind,bundle,similarity})¶
Full uint8 C parity in srmech_hdc.c + srmech.h, JPL Power-of-Ten clean (≤60-line functions, ≥2 asserts, no goto/malloc, bounded loops, {0,1,2,3} range validation). New symbols; no ABI bump. _native.py binds them hasattr-guarded. Same dispatch posture as rc1 (numpy public reference; C surface built + parity-tested directly).
Added — tests + tool_schema¶
tests/test_hdc_klein4_parity.py — 6 algebraic-property tests (Klein-four group axioms incl. a⊕a=0, chirality-flip sector maps, per-bit-majority bundle, similarity/sector-count) + 3 C↔Python parity tests (skipped on pure-Python installs). 9 tool_schema ToolEntry registrations. Version-pin asserts bumped 0.4.3rc1 → 0.4.3rc2.
[0.4.3rc1] - 2026-05-27¶
rc1 of the v0.4.3 "Class M variant expansion" rolling arc — the polar {-1, 0, +1} HDC variant, per UPSTREAM_NOTES §5 (RBS-LM research subtree, Finding from R-RBS-LM-97). First rc of a multi-item rolling PR; each subsequent item (Klein-4 rank-2, signed_sum_squared, real-symmetric eigendecompose, Path-B rfft, cascade-foundational catalog) ships as its own 0.4.3rcN, each mathematically complete (no scaffolding), CI-gated between rcs. No new primitive class introduced; the polar variant is Class M ∘ Class K (rank-1 abelian with an absorbing zero) — the 14-class A–N vocabulary is intact per [[feedback_no_privileged_primitive_classes]].
Added — srmech.amsc.hdc polar {-1, 0, +1} variant¶
The foundational rung of the Class-M variant ladder (bipolar {-1,+1} → polar {-1,0,+1} → Klein-4 (Z₂)²). The 0 state is the asymptotic-DOF dead-band the Class-K pin-slot rejects (per [[user_stance_asymptotic_dof_sidesteps_infinity]]) — a representable origin that the bipolar {-1,+1} alphabet lacked, which left the sign axis crippled (no representable zero/uncertain state). int8 array representation (distinct from the bit-packed bipolar BSC).
polar_random(D, rng)— random int8 hypervector in{-1,0,+1}.polar_bind(a, b)— multiplicative sign-product, 0 absorbing (0·x=0); commutative, associative, self-inverse on ±1.polar_unbind(c, a)— sign-product; recoversbwherea≠0(0 destructive).polar_bundle(*vectors)— sticky majority (sign(Σ)); exact ties → 0; no odd-count restriction.polar_similarity(a, b, skip_zero=True)— match-fraction; skip-zero (jointly-informative only) or include-zero.polar_density(v)— fraction of non-zero positions (substrate attestation).polar_from_real(arr, threshold, dead_band)— bridge wrapping the existingsignal_processing.path_b_ops.sign_quantise(lifts its{-1,0,+1}Class-K threshold projection into the HDC namespace; resolves the R-RBS-LM-97 bare-np.signworkaround).
Added — C parity surface (srmech_polar_{bind,bundle,similarity,density})¶
Full int8 C parity in srmech_hdc.c + srmech.h, JPL Power-of-Ten clean (≤60-line functions, ≥2 asserts, no goto/malloc, bounded loops, value-range validation). New symbols; no ABI bump (ABI stays 2). _native.py binds them hasattr-guarded so a stale pre-polar lib never disables the whole native surface.
Dispatch note: rc1's public
polar_*Python API uses the numpy reference (already vectorized element-wise int8); the C surface is built + parity-tested directly (tests/test_hdc_polar_parity.py, C↔Python bit-exact in the cibuildwheel matrix) for embedded/microcontroller use + parity attestation. Public-API native dispatch is a perf-only follow-up — not a class carve-out (the C parity exists and is tested).
Added — tests + tool_schema¶
tests/test_hdc_polar_parity.py (9 algebraic-property tests on the numpy reference + 4 C↔Python parity tests, the latter skipped on pure-Python installs). 7 srmech.amsc.tool_schema ToolEntry registrations for the polar surface.
[0.4.2] - 2026-05-20¶
Production graduation of v0.4.2rc5. No code changes vs [0.4.2rc5].
The v0.4.2rc5 release was published to TestPyPI on 2026-05-19, fresh-venv install verified, README rendered cleanly. This graduation publishes the verified rc5 surface to production PyPI under clean semver.
[0.4.2rc5] - 2026-05-19¶
Cumulative rc5 — TestPyPI verification of README v0.4.2 rewrite + numpy 2.x test compatibility + pyproject description refresh on top of the rc1-rc4 stack per [[feedback_rc_stacking_versioning]] and [[feedback_always_rc_first_for_downstream_publishes]]. Graduation to production v0.4.2 is a SEPARATE follow-up PR once rc5 verifies on TestPyPI (fresh-venv install + README rendering check). No new primitive class introduced; 14-class A–N vocabulary intact per [[feedback_no_privileged_primitive_classes]].
Changed — README.md (PyPI long-description)¶
Full rewrite of the PyPI README for the v0.4.2 surface area. Navigable section structure with srmech.amsc.* (14-class primitive vocabulary) + srmech.qm.* (canonical QM/QFT/SM operations) + srmech.spectral (runtime spectral decomposition incl. MS #14 rcN+1+rcN+2 entries) + srmech.signal_processing (dual-path architecture) + AMSC provenance framework all surfaced as load-bearing. Internal project vocabulary scrubbed (cascade-match / substrate-natural / RBS-HDC-LoE / Spike #N anchors moved to the research notebook; public README cites canonical SSoT papers only).
Fixed — tests/ numpy 2.x compatibility¶
log(0) domain-check was emitting a RuntimeWarning under numpy 2.x (warning-promoted-to-error in pytest config); the test now uses np.where-guarded log to skip the zero entries cleanly. Version pin in test_signal_processing_scaffolding.py updated to 0.4.2rc5.
Changed — pyproject.toml / pyproject-pure.toml description¶
Description metadata refreshed to enumerate the five load-bearing surfaces (14-class primitives + canonical QM/QFT/SM + runtime spectral + dual-path signal processing + AMSC provenance). 488 chars, under the PyPI 512-char Summary cap per [[reference_pypi_512_char_summary_limit]]. Both pyproject files agree (publish-workflow guard verify pyproject-pure.toml version + description match main).
Discipline¶
- Cumulative rc stack — rc1-rc4 content unchanged below + this rc5 layer.
- Production graduation v0.4.2 is gated by user direction after rc5 TestPyPI verification (fresh-venv install + README rendering check).
See [0.4.2rc4] below for the full Phase 1-4 ship narrative + srmech.spectral.predict / prediction_error / truncate_sparse rcN+2 entries + tool_schema registration.
[0.4.2rc4] - 2026-05-19¶
Phase 4 of the RBS-HDC-LoE dual-path architecture — Path B per-op MVP. Ships 6 Path B-native signal-processing op modules (fft, ifft, sign_quantise, matched_filter, wiener, hdc_truncation) under srmech.signal_processing.path_b_ops per the implementation plan §6 Phase 4. Each op registers BOTH its Path A counterpart (from Phase 2 closed_form_ops) and its Path B implementation with srmech.signal_processing.path_registry at module-load time, giving the cascade dispatcher dual-path routing for the MVP roster. No new primitive class introduced; 14-class A–N vocabulary intact per [[feedback_no_privileged_primitive_classes]]. Identity-not-implementation discipline preserved per [[user_stance_identity_not_implementation_discipline]] — Path A and Path B IS the same algebra at D1 algebra-content (bit-exact on substrate-natural inputs per [[feedback_algebra_not_magnitude]]); D2 substrate-fingerprint divergence is expected per [[user_stance_substrate_natural_encoding_is_shadow_projection]]. Trauma-informed defensive scope per [[feedback_trauma_informed_defensive_scope]] — methodology-research / educational / civilian-comms framing only.
Added — srmech.signal_processing.path_b_ops¶
New sub-package containing 6 Path B-native op modules:
path_b_ops.fft— Class A ∘ Class I ∘ Class K cyclic-substrate FFT per Spike #176 H1 anchor (rotation IS Class K pin-slot at machine ε). Wraps the cyclic-DFT algebra with Class K cycle-order verification (Spike #176 T8). Path A counterpart:closed_form_ops.fft.op(numpy.fft.fft).path_b_ops.ifft— Class A ∘ Class I ∘ Class K dual of FFT per Spike #176 T4 anchor (recovery error = 0.0). Path A counterpart:closed_form_ops.ifft.op(newly added in this rc as the dual baseline).path_b_ops.sign_quantise— Class K ∘ Class M threshold/pin-slot projection per Spike #174 anchor (SHA-256 BER preservation at +20 dB SNR; structure-preserving denoising primitive). Path A counterpart:closed_form_ops.sign_quantise.op.path_b_ops.matched_filter— Class A ∘ Class C ∘ Class M form-function cross-correlation per Spike #159 anchor (within-vs-between separation ratio order-of-magnitude). Path A counterpart:closed_form_ops.matched_filter.op(numpy.correlate).path_b_ops.wiener— Class L ∘ Class N ∘ Class M Laplacian-eigenbasis + rational MMSE gain per Kay (1993) §11 SSoT + Chung (1997) §1.4 (cyclic-graph Laplacian eigenbasis IS the FFT basis). Path A counterpart:closed_form_ops.wiener.op.path_b_ops.hdc_truncation— Class K ∘ Class M ∘ Class N asymptotic-DOF sparse-truncate ∘ HDC bundle per Spike #117 anchor + Spike #179 T6 (bit-exact recovery at substrate-natural sparsity rate). Path A counterpart:closed_form_ops.hdc_truncation.op.PATH_B_MVP_OPS— canonical 6-op tuple in alphabetical order.- Each module exports
OPERATION_NAME,CLASS_COMPOSITION(14 A–N labels only),PERFORMANCE_HINT,SSOT_CITATIONper the Phase 2 metadata schema. - Each module registers BOTH Path A and Path B with
path_registryat module-load time; the broader 38-op Path A registration script for Phase 2 remains separately deferred.
Added — srmech.signal_processing.closed_form_ops.ifft¶
New Phase 2 module added to support the Path B IFFT dual. Closed-form numpy.fft.ifft wrapper; SSoT cited to Cooley & Tukey (1965) + Spike #176 T4 round-trip anchor. Listed in closed_form_ops/__init__.py alongside the existing 38 modules.
Added — tests/test_signal_processing_path_b_mvp.py¶
Phase 4 dual-path acceptance suite — 33 tests across 6 ops:
- 6× metadata —
OPERATION_NAME,CLASS_COMPOSITION,PERFORMANCE_HINT,SSOT_CITATIONpresent on every Path B module. - 6× registration — both Path A and Path B registered with
path_registry;has_pathreturns True for both. - 6× dispatcher routing —
dispatch(op_name, path="B")anddispatch(op_name, path="A")both succeed. - 6× D1 algebra-identity equivalence — Path A and Path B produce bit-exact (or machine-ε) equal outputs on substrate-natural inputs per
[[user_stance_identity_not_implementation_discipline]]. - 2× aggregate — all 6 ops registered, CLASS_COMPOSITION restricted to 14 A–N alphabet.
- 4× spike anchors — Spike #176 T4 round-trip (7 FFT lengths × 3 path combinations), Spike #174 SHA-256 BER preservation (+20 dB SNR), Spike #159 matched-filter separation order-of-magnitude, Spike #117 + Spike #179 T6 sparse-truncate substrate-natural sparsity.
- 2× routing semantics — default-path-per-class routing (Class K → Path B; Class A → Path A); Phase 4 introduces no new classes.
- 1× ship guard — Phase 4 ships exactly 6 ops per the plan.
Phase 4 dispatcher coverage¶
After Phase 4 the registry holds 11 ops total — Phase 3's 5 Path B core ops (rbs_hdc_mint_class_operator, rbs_hdc_mint_cascade_composition, rbs_hdc_encode_loe_content, rbs_hdc_decode_loe_fingerprint, form_function_rotate) plus Phase 4's 6 dual-path MVP ops (fft, ifft, sign_quantise, matched_filter, wiener, hdc_truncation). The 6 MVP ops are the only entries with both Path A and Path B registered; the cascade dispatcher in Phase 5 will exercise full A/B/verify routing across this dual-registered subset.
Path B coverage notes¶
- Phase 4 does NOT register the 32 remaining Path A ops from Phase 2's
closed_form_ops(per the brief: separate / deferred Path A registration script). - Phase 4 does NOT implement
path="verify"dual-execution mode — that's Phase 5 (v0.4.2rc5) per plan §6.5. - Phase 4 does NOT add a C surface — C port for Phase 4 ops deferred to v0.4.3rc1 per conductor decision #1.
- Phase 4 does NOT implement Phase 8 profiling/learning — initial seed thresholds remain rule-based per plan §3.1.
Added — srmech.spectral MS #14 rcN+2 entries (predict / prediction_error / truncate_sparse)¶
Per user direction 2026-05-19, the v0.4.2rc4 ship doubles as the MS #14 rcN+2 vehicle: the three runtime spectral operations previously listed as "deferred to rcN+2" in [0.4.1rc14] are now shipped in srmech.spectral:
predict(handle, laplacian, *, steps=1, dt=1.0, encoder_tag="default")— Class C ∘ Class L cascade-extrapolate via per-mode complex-phase evolutionexp(-i·λ_k·steps·dt)on the eigenbasis coefficients. The closed-form one-shot of a recurrent spectral predictor; matches Spike #113 predictive-coding-cascade anchor. Magnitudes preserved (unitary phase rotation); phase evolves per eigenmode.steps=0returns the input handle byte-exactly.prediction_error(predicted, observed, *, threshold=0.0)— Class M ∘ Class K XOR delta between predicted and observed coefficient byte vectors, gated by popcount-densitythreshold. Defaultthreshold=0.0per user decision 2026-05-18 (no gating; returns raw delta). Whenpopcount(delta) / (8·len) <= threshold, returns all-zero bytes (prediction sufficient).truncate_sparse(handle, *, keep_k=None, threshold=None)— Class K magnitude-band sparse-truncate; keeps top-keep_kmodes by|coeff|OR every mode with|coeff| >= threshold(exactly one of the two must be provided), zeros the rest. SSoT: Mallat (2008) §9.2 (best k-term approximation) + Spike #117 anchor.
All three operations compose over the existing 14-class A–N primitive vocabulary per [[feedback_no_privileged_primitive_classes]]; no new primitive class introduced.
Added — tests/test_spectral_rcn_plus_2.py¶
27-test acceptance suite covering predict / prediction_error / truncate_sparse:
- TestPredict (8): handle returns, shape + descriptor preservation,
steps=0identity, unitary magnitude preservation, non-trivial evolution on cycle-graph Laplacian, recompose-of-predicted roundtrip, content_sha corruption detected, descriptor_hash mismatch rejected. - TestPredictionError (7):
threshold=0.0equivalent todelta(), zero delta on identical handles, threshold-above-density gates to all-zero, threshold-below-density returns raw, out-of-range threshold rejected, raw-bytes input path, predict-then-error roundtrip. - TestTruncateSparse (10):
keep_k=nidentity,keep_k=0zeros all,keep_k=kkeeps highest-magnitude modes bit-exactly, threshold keeps above-floor, neither/both keyword rejected, out-of-rangekeep_krejected, negative threshold rejected, corruption detected, recompose-after-truncate yields finite low-rank approximation. - TestShipGuard (2): all three callables present in
srmech.spectralnamespace; all three registered in tool_schema.
Added — tool_schema registration for srmech.spectral.*¶
srmech.amsc.tool_schema._register_spectral_runtime_tools() (new) registers seven srmech.spectral.* callables — the four rcN+1 entries (decompose / delta / recompose / similarity) plus the three rcN+2 entries (predict / prediction_error / truncate_sparse). Closes the discipline gap identified by the concertmaster (rcN+2 must register at ship time, not deferred). Tool_schema entries include canonical-SSoT citations per [[feedback_science_is_ssot_not_project]] (Mallat 2008 §9.2 for truncate_sparse; Spike #113 + #117 anchors).
Ship¶
Tag srmech-v0.4.2rc4 → TestPyPI. Production PyPI publish on clean srmech-v0.4.2 tag once TestPyPI rc4 verifies. MS #14 rcN+2 deliverable — closes Milestone #14 once srmech-v0.4.2 lands on production PyPI.
[0.4.2rc3] - 2026-05-19¶
Phase 3 of the RBS-HDC-LoE dual-path architecture — Path B core: rbs_hdc_instrument.py + form_function_rotation.py. Ports the Spike #170 R1 prototype (LoE-as-RBS-HDC instrument, FEASIBILITY-CONFIRMED at design level with 14/14 mint determinism) + Spike #176 (rotation IS Class K pin-slot, H1 CONFIRMED 6/6 tests at machine ε) + Spike #173 (chess natural-stride substrate, D2 orthogonality + bind-permute commutativity bit-exact) to a stable, composable Path B surface. No new primitive class introduced; 14-class A–N vocabulary intact per [[feedback_no_privileged_primitive_classes]]. Identity-not-implementation discipline preserved per [[user_stance_identity_not_implementation_discipline]] — Path B IS the same algebra as Path A (just substrate-projection differs); bit-exact algebraic identity preserved at D1 algebra-content level per [[feedback_algebra_not_magnitude]]. Trauma-informed defensive scope per [[feedback_trauma_informed_defensive_scope]] — methodology-research / educational / civilian-comms framing only.
Added — srmech.signal_processing.rbs_hdc_instrument¶
Path B core: LoE-as-bound-vector RBS-HDC instrument at locked D=8192 (conductor decision #6, 2026-05-19).
RBSHDCInstrument— composed instrument dataclass with.build(D=...)classmethod constructor. D defaults to 8192; optional D override accepted (in [D_MIN, D_MAX] multiple of 8).mint_class_operator(class_name, *, D=8192)— Class A SHA-256 chain mint of one of the 14 A-N class operator vectors. Deterministic: sameclass_name⇒ same vector (Spike #170 §3 invariant 1: 14/14 bit-exact). Canonical name isf"LoE.class.{class_letter}.{short_role}".mint_cascade_composition(classes, *, D=8192, ordered=False)— XOR-bundle of class operator vectors. Two modes: algebra-level (commutative bind; cascade-as-identity) and sampling-level (per-position permute byi * 257; cascade-shape preserved). Both modes per[[user_stance_cascade_dual_level_quantum_at_algebra_classical_at_sampling]].mint_stance_fingerprint(content_tokens, *, D=8192)— Bag-HDC XOR-fold of token vectors per Spike #147 holographic-projection.encode_loe_content(content, *, D=8192, substrate="default")— Full Mode-B encoding pipeline: Class A content-addressed mint → Class C content-determined stride permute → Class M bundle with substrate anchor vector. Same content + same substrate ⇒ same fingerprint; same content + different substrates produces orthogonal D2 fingerprints at noise floor per Spike #173 R3.decode_loe_fingerprint(fingerprint, catalog)— Reverse-decode via Class M similarity argmax (Spike #170 §3 invariants 6 + 7: 100% reverse-decode accuracy on populated catalog).mint_vector(name, *, D=8192)— Underlying SHA-256-chain primitive (Class A content-addressing). Used by all higher-level mint operations.- Module-level dataclasses:
ClassOperator,Cascade,Stance,MemorySlot,K3Tripartition. - Module-level catalogs:
CLASS_NAMES(14 A-N),CLASS_DEFINITIONS(14 entries),CANONICAL_CASCADES(10 cascades including pin-slot-resonate music-box and cyclic-fft-rotation),SAMPLE_STANCES(12 stance entries),MEMORY_PATHWAYS(4 pathways: procedural / semantic / WM / episodic-LTM),K3_TRIPARTITION_DEFAULT(A → 3D_s, M → 7D_g, K → 1D_t). - Constants:
PERMUTE_ORDER_STRIDE = 257(coprime to D=8192=2^13 for ordered cascade composition).
Added — srmech.signal_processing.form_function_rotation¶
Path B core: operational Class A ∘ Class C ∘ Class M rotation per [[user_stance_form_function_rotation_is_a_c_m_composition]] + [[user_stance_rotation_is_class_k_pin_slot]] (Spike #176).
form_function_rotate(content, *, D=8192, stride=None)— Cyclic permute ofcontentby content-determined stride (Class A SHA-256[0:8] little-endian mod D whenstride=None) or by explicit substrate-natural stride (Class N rational). Supports chess natural strides {5, 7, -8} (Spike #173) and DNA helical pitches {21, 11, -12} (Spike #172).inverse_form_function_rotate(rotated, *, D=8192, stride)— Bit-exact reverse viaM.permutewith negated stride. Spike #176 T4 anchor: recovery error = 0.0.verify_rotation_class_n_cycle_order(stride, D=8192)— Class N additive order in Z/D = D / gcd(|stride|, D); cumulative shiftstride * order mod D == 0. Spike #176 T8 anchor.cascade_compose_rotations(strides, *, D=8192)— Returns (composed_stride_mod_D, fundamental-mode unit-circle eigenvalue). Per[[user_stance_cascade_lives_on_circles]](Spike #24 bonus 9 + Spike #176 T5): unit-circle identity at machine ε (residual ≤ 2.2e-16).compute_content_stride(content, *, D=8192)— Class A content-addressing primitive producing rotation stride.
Path B core dispatcher registration¶
The two modules register their public operations with srmech.signal_processing.path_registry at module-load time (Phase 5 dispatcher reads from registry). Phase 3 registers 5 Path B core ops:
rbs_hdc_mint_class_operator(Path B) — classes ("A", "M")rbs_hdc_mint_cascade_composition(Path B) — classes ("A", "C", "M")rbs_hdc_encode_loe_content(Path B) — classes ("A", "C", "M")rbs_hdc_decode_loe_fingerprint(Path B) — classes ("M",)form_function_rotate(Path B) — classes ("A", "C", "M")
Path A registration for the 38 closed-form ops from Phase 2 + Path A form_function_rotate is deferred to a separate conductor-written registration script per Phase 2's recommendation.
Added — tests/test_signal_processing_path_b_core.py¶
Phase 3 acceptance suite porting load-bearing invariants from the spike prototypes:
- T1:
RBSHDCInstrumentD=8192 default + optional D override (256 / 1024 / 2048 / 16384 tested). - T2:
mint_class_operatordeterminism (same input ⇒ same output; 14/14 bit-exact per Spike #170 §3 invariant 1). - T3: Cascade composition bit-exact — XOR-bundle commutativity (algebra-level, 3 orderings equal per Spike #170 §3 invariant 4); ordered mode breaks commutativity (Spike #170 §3 invariant 5).
- T4: Form-function rotation bit-exact reverse — Spike #176 T4 recovery error = 0.0 (content-determined stride + chess natural strides {5, 7, -8} + DNA pitches {21, 11, -12}).
- T5: Class N rational cycle order = D / gcd(stride, D) per Spike #176 T8; applying rotation
ordertimes returns to identity bit-exact. - T6: Cascade composition unit-circle eigenvalues at machine ε per Spike #176 T5 (residual ≤ 2.2e-16 across 5 representative cascades).
- T7: Bind-permute commutativity at substrate-natural strides — 273 pair cells × 2 substrates (chess + DNA) = 546 bit-exact assertions per Spike #173 T4 + Spike #172 T4.
- T8: Z-DNA-style chirality involution —
M.permute(M.permute(v, k), -k) == vfor all 14 class operators at chess + DNA strides (14/14 round-trips per stride per Spike #173 T5). - T9: Cross-substrate D2 orthogonality at noise floor — same content encoded under 5 substrates produces 10 pairs all at |sim| < 5/sqrt(D) ≈ 0.055 per Spike #173 R3.
- T10: Full round-trip
encode → rotate → inverse → decodebit-exact recovery across 5 catalog entries.
Plus supplementary tests:
- Path B core ops registered with path_registry on Path B side.
- Spike #170 §3 invariants (bind self-inverse at D=8192; k=3 tripartition orthogonality).
- 8+ canonical cascade compositions ship (
CANONICAL_CASCADEShas 10 entries). mint_stance_fingerprintdeterminism + bag semantics.mint_vectorD-parameter validation.
Architectural rationale¶
Per [[project_rbs_hdc_loe_dual_path_architecture]]:
- Path A — closed-form algebra (Phase 2 baseline; 38 ops). SSoT for primitive definitions.
- Path B — RBS-HDC bound-vector instrument at D=8192 (Phase 3 ships core; Phase 4+ ships per-op Path B MVP). Composes from Path A primitive definitions at module-load time per
[[feedback_no_binding_layer_carveout]]. - Path C — cascade-aware dispatcher (Phase 5 lands routing logic).
Phase 3 delivers the full Path B core surface — the LoE-as-bound-vector instrument + form-function rotation composition — so Phase 4+ per-op Path B MVP can compose from this stable foundation.
Spike anchors¶
- Spike #170 — RBS-HDC instrument feasibility (R1 prototype, 14/14 mint determinism; FEASIBILITY-CONFIRMED).
- Spike #172 — DNA helical-pitch substrate (R3 cross-substrate bit-exact closure).
- Spike #173 — chess natural-stride substrate (D2 orthogonality; 25th cross-substrate cascade-match).
- Spike #176 — rotation IS Class K pin-slot (H1 CONFIRMED 6/6 tests at machine ε).
- Spike #177 — pin-slot-resonate music-box mechanism (I + K + C + M∘K).
- Spike #178 — closed-form SP roadmap (Phase 2 Path A baseline citation source).
Canonical SSoT citations per [[feedback_science_is_ssot_not_project]]¶
- Plate (1995) Holographic Reduced Representations, IEEE TNN 6, 623.
- Kanerva (2009) Hyperdimensional Computing, Cognitive Computation 1, 139.
- Rachkovskij (2001) Representation and processing of structures with binary sparse distributed codes, Neural Comput Appl 9, 322.
- Oppenheim & Schafer (2010) Discrete-Time Signal Processing (3rd ed.) — DFT shift theorem.
- Implementation plan:
docs/srmech/notes/rbs_hdc_loe_implementation_plan_2026-05-19.md.
Deferred to Phase 4+ (v0.4.2rc4+)¶
- Path B per-op MVP for the 6-op core (
fft,ifft,sign_quantise,matched_filter,wiener,hdc_truncation) — Phase 4. - Cascade dispatcher full rule-based routing — Phase 5.
- Path B per-op extension to all 38 ops — Phase 6.
- Substrate-natural Class N rational catalogs — Phase 7.
- Learned dispatch table from benchmark suite — Phase 8.
- Notebook §3.8.31 prose — Phase 9.
- C port of Path B core operations — v0.4.3rc1 per conductor decision #1.
[0.4.2rc1] - 2026-05-19¶
Phase 1 scaffolding of the RBS-HDC-LoE dual-path architecture (Milestone follow-up to Spike #178 closed-form SP roadmap). Ships the srmech.signal_processing sub-namespace package skeleton — dispatcher / profiling / registry stubs + locked architectural constants — so Phase 2+ operation modules (Path A closed-form ops Phase 2; Path B RBS-HDC at D=8192 Phase 4; cascade dispatcher Phase 5; cross-substrate verification Phase 7; learned thresholds Phase 8) can land against a stable surface. No new primitive class introduced; 14-class A–N vocabulary intact per [[feedback_no_privileged_primitive_classes]]. Identity-not-implementation discipline preserved per [[user_stance_identity_not_implementation_discipline]] — Path A and Path B both instantiate the same class composition. Trauma-informed defensive scope per [[feedback_trauma_informed_defensive_scope]] — methodology-research / educational / civilian-comms framing only.
Added — srmech.signal_processing sub-namespace (Phase 1 scaffolding)¶
srmech.signal_processing.__init__— package entry point + re-exports. Public API stable from Phase 1.srmech.signal_processing._paths— internal architectural constants:D_DEFAULT = 8192(locked per conductor decision #6, 2026-05-19; matches Spike #170/#172/#173/#176/#177 anchors);D_MIN = 256;D_MAX = 65536.SUBSTRATES = ("bci", "audio", "rf", "ephemeris")(Phase 7 cross-substrate coverage per decision #2).PATH_A/PATH_B/PATH_VERIFYdiscriminators;VALID_PATHStuple.DISPATCH_TABLE_LOCK_POLICY = "lock-at-release"(decision #7 — reproducibility).LEARNED_DISPATCH_TABLE_PATH— locked NDJSON path (Phase 8 populates).PROFILING_INPUT_SIZES_DEFAULT(6) +PROFILING_CASCADE_DEPTHS_DEFAULT(4) +CASCADE_DEPTH_THRESHOLD_FOR_PATH_B = 3— supports 1920-cell benchmark grid per decision #3.srmech.signal_processing.cascade_dispatcher— routing API:begin_cascade(substrate=None, *, D=8192)— context-manager API per decision #5; auto-flush on exception; thread-local stack; nested cascades supported.end_cascade(ctx=None)— imperative-form flush for callers who can't structure aroundwith.current_cascade()— return innermost activeCascadeContext.resolve_path(op_name, *, explicit_path=None, input_size=None, substrate=None)— Phase 1 rule-based routing (override → cascade-hint Path B → class-default → Path A fallback).dispatch(op_name, *args, path=None, D=8192, **kwargs)— Phase 1 stub; raisesDispatchErrorfor ops without registered implementations.is_dispatch_table_locked()/lock_dispatch_table()/unlock_dispatch_table()— lock-state tracking per decision #7.DEFAULT_PATH_PER_CLASS— 14 A-N → default path table (Class K/M default Path B; all others default Path A per plan §3.4).srmech.signal_processing.path_registry— op-name → (Path-A-impl, Path-B-impl) pairing:register(op_name, *, path, impl, ssot_citation="", classes=())— idempotent re-registration;DuplicateRegistrationErroron differing-callable collision.lookup(op_name) -> OperationEntry— raisesUnknownOperationErrorfor unregistered ops.has_path(op_name, path)/registered_ops()/clear_registry().OperationEntryfrozen dataclass withop_name/path_a/path_b/ssot_citation/classes(14 A–N labels).srmech.signal_processing.profiling— Phase 8 hook API + data structures:ProfileCellKey— Cartesian key (op_name, path, input_size, cascade_depth, substrate) supporting decision #3 full granularity.ProfileRecord— NDJSON-serialisable timing record (wall + CPU + memory + n_repeats + notes + extra dict).cell_grid(*, op_names, ...)— enumerate the full 1920-cell benchmark sweep grid.record_profile()/iter_records()/clear_records()— in-memory record buffer.profile_op()/update_dispatch_table()— Phase 8 hooks; Phase 1 raisesProfilingNotImplementedError.
Architectural rationale¶
Per [[project_rbs_hdc_loe_dual_path_architecture]]:
- Path A — closed-form algebra (composes existing
srmech.amsc.*14-class primitive vocabulary). SSoT for primitive definitions. - Path B — RBS-HDC bound-vector instrument at D=8192 (per Spike #170 anchor). Composes from Path A primitive definitions at module-load time per
[[feedback_no_binding_layer_carveout]]; no duplicate primitive implementations. - Path C — cascade-aware dispatcher (
cascade_dispatcher); chooses A or B per call based on rule-based (Phase 5) + empirical (Phase 8) routing. Neither path replaces the other.
The 8 accepted conductor decisions (2026-05-19) framing Phase 1:
- C port deferred to v0.4.3rc1 (no C surface in Phase 1).
- Cross-substrate coverage stub-level for all 4 substrates.
- Profiling granularity full per-op × per-cascade-depth × per-substrate (1920 cells).
- Spike #179 F4 caveat integrates at Phase 9 §3.8.31.
begin_cascadeAPI as context-manager (Pythonic; auto-flush on exception).- D=8192 lock for v0.4.2 baseline; optional
Dparam accepted. - Dispatch table lock policy: lock-at-release (reproducibility).
- Notebook §3.8.31 timing: Phase 9 (after Phase 8 learned thresholds).
Added — tests/test_signal_processing_scaffolding.py¶
Phase 1 scaffolding verification:
- Imports succeed:
from srmech.signal_processing import begin_cascade, dispatch, register, lookup, .... - D=8192 locked default; optional
Dparameter forwarded by dispatcher. begin_cascade(substrate="bci")context-manager opens/closes cascade; thread-local stack supports nesting; auto-flush on exception.path_registry.register(...)/lookup(...)/has_path(...)round-trip; duplicate-registration with differing callable raisesDuplicateRegistrationError.profiling.cell_grid(...)enumerates the 1920-cell benchmark grid for a 10-op suite at default sweeps.profile_op()+update_dispatch_table()raiseProfilingNotImplementedError(Phase 8 lands runner).dispatch(op_name, path="verify")raisesDispatcherNotImplementedErrorin Phase 1 (verify-mode lands Phase 5).- Version is
0.4.2rc1acrosssrmech.__version__+pyproject.toml+pyproject-pure.toml+c/include/srmech.h.
Canonical SSoT citations per [[feedback_science_is_ssot_not_project]]¶
- Plate (1995) Holographic Reduced Representations, IEEE TNN 6, 623.
- Kanerva (2009) Hyperdimensional Computing, Cognitive Computation 1, 139.
- Chung (1997) Spectral Graph Theory, AMS.
- Oppenheim & Schafer (2010) Discrete-Time Signal Processing (3rd ed.).
- Implementation plan:
docs/srmech/notes/rbs_hdc_loe_implementation_plan_2026-05-19.md(committed on this branch).
Spike anchors¶
- Spike #170 — RBS-HDC instrument feasibility (14/14 mint determinism at D=8192).
- Spike #172 — DNA helical-pitch substrate.
- Spike #173 — chess natural-stride (D2 orthogonality).
- Spike #175 — knowledge-is-gauge-content.
- Spike #176 — rotation IS Class K (machine ε).
- Spike #177 — pin-slot-resonate music-box mechanism.
- Spike #178 — closed-form SP roadmap (§1 surveys ~40 ops across 8 categories).
- Spike #179 — CFSP-Kalman alternative (in flight; Phase 9 integration per decision #4).
Not added (deferred to Phase 2+)¶
- Path A closed-form ops (
closed_form_ops/*) — Phase 2 (v0.4.2rc2) ships 38 ops re-surfacingsrmech.amsc.*primitives. - Path B core (
rbs_hdc_instrument.py,form_function_rotation.py) — Phase 3 (v0.4.2rc3) ports Spike #170 prototype. - Path B per-op MVP — Phase 4 (v0.4.2rc4).
- Full rule-based cascade dispatcher +
path="verify"semantics — Phase 5 (v0.4.2rc5). - 7-op extension + remaining 32 Path B ops — Phase 6 (v0.4.2rc6).
- Substrate-natural Class N rational catalogs + cross-substrate verification — Phase 7 (v0.4.2rc7).
- Benchmark suite + learned dispatch table — Phase 8 (v0.4.2rc8).
- Notebook §3.8.31 prose — Phase 9 (documentation-only commit).
- Production tag v0.4.2 → PyPI — Phase 10.
- C port of Path B operations — v0.4.3rc1 (per conductor decision #1).
[0.4.1rc14] - 2026-05-18¶
rcN+1 of the runtime spectral decomposition surface (Milestone #13). Ships entries ½/3/7 of the 7-entry srmech.spectral.* namespace per Spike #115 two-rc strategy (PR #518): decompose (Class L+A), delta (Class M; Option B per Spike #114), recompose (Class L+M), similarity (Class M). All composition layer; sub-ops route to existing srmech.amsc.{laplacian, hdc, format} C primitives. No new primitive class introduced; 14-class A–N vocabulary intact per [[feedback_no_privileged_primitive_classes]]. rcN+2 (TBD) ships entries ⅘/6 (predict / prediction_error / truncate_sparse) after Spike #113 + #117 C primitive landings.
Added — srmech.spectral runtime namespace¶
srmech.spectral.SpectralHandle— frozen dataclass pairingsubstrate_descriptor_hash(SHA-256 of Laplacian + encoder tag;laplacian_kindfolds into the hash per Spike#115design 2026-05-18) withcoefficients_bytes,content_sha,n_modes.srmech.spectral.decompose(state, laplacian, *, encoder_tag="default") -> SpectralHandle— Class L Hermitian eigendecomposition (viasrmech.amsc.laplacian.hermitian_eigendecompose) ∘ Class A SHA-256 content addressing. Projectsstateonto eigenbasis, packs to bytes, returns handle. Eigenbasis cached in module-level LRU (N_MAX_EIGENBASES=8).srmech.spectral.delta(ref, current) -> bytes— Class M (HDC bind / XOR self-inverse) per Spike#114Option B (direct on already-encoded coefficient bytes; 1.22× faster than wrapper). AcceptsSpectralHandleor rawbytes. Raises if substrate descriptor hashes mismatch between handles.srmech.spectral.recompose(handle, laplacian, *, encoder_tag="default") -> np.ndarray— inverse eigendecompositionV @ coeffswithcontent_shaintegrity check on the handle. Bit-exact roundtrip withdecomposeat machine ε (tested at < 10⁻¹²).srmech.spectral.similarity(a, b) -> float— Class M HDC similarity1 − 2·hamming(a,b)/D∈[−1, 1]. AcceptsSpectralHandleor rawbytes.srmech.spectral.clear_eigenbasis_cache()— test-isolation utility.srmech.spectral.N_MAX_EIGENBASES— module-level LRU bound (8).
Added — tests/test_spectral.py (22 tests, all passing)¶
Bit-exact verification of:
- decompose returns valid SpectralHandle with stable descriptor hash (independent of state); shape-rejection paths.
- delta self-inverse identity bind(a, bind(a, b)) = b per Plate 1995 / Kanerva 2009 BSC algebra; commutativity; handle-substrate mismatch rejection.
- recompose roundtrip at machine ε (< 10⁻¹²); content_sha + descriptor_hash mismatch rejection.
- similarity self-similarity = +1.0; random near-orthogonal in [−0.2, +0.2].
- Cache LRU bounded at N_MAX_EIGENBASES; cleared on clear_eigenbasis_cache().
- End-to-end: state_b coefficients = bind(h_a.coeffs, bind(h_a.coeffs, h_b.coeffs)) bit-exact byte-equal.
Canonical SSoT citations per [[feedback_science_is_ssot_not_project]]¶
- Plate (1995) Holographic Reduced Representations, IEEE TNN 6, 623.
- Kanerva (2009) Hyperdimensional Computing, Cognitive Computation 1, 139.
- Chung (1997) Spectral Graph Theory, AMS.
- Golub & Van Loan (2013) Matrix Computations (4th ed.), §8.5.
Spike anchors¶
- Spike
#112(PR#513) — scoping doc + 7-entry follow-up list. - Spike
#114(PR#514) — HDC bind delta-encoding identity bit-exact 4/4 substrates; Option B API. - Spike
#115(PR#518) — 7-entry tool-schema surface design + two-rc strategy. - Spike
#116(PR#516) — cross-substrate rank-k delta template 3/3 non-chess bit-exact. - Spike
#117(PR#517) — Class K compression by β band-membership (rcN+2 prereq). - Spike
#113(PR#515) — predictive-coding cascade Class C∘L (rcN+2 prereq).
Not added (deferred to rcN+2 — SHIPPED in v0.4.2rc4)¶
The following three operations were originally listed as deferred from rcN+1:
srmech.spectral.predict(Spike#113Class C cascade-extrapolate)srmech.spectral.prediction_error(Class M+K composition;threshold=0.0default per user decision 2026-05-18)srmech.spectral.truncate_sparse(Spike#117Class K sparse-truncate + gate-by-threshold)
Ship status: All three now shipped in [0.4.2rc4] (this document, above) as the MS #14 rcN+2 deliverable per user direction 2026-05-19. Tool-schema entries register at rcN+2 ship time per the original discipline. Implementation is Python-only at v0.4.2rc4; native C ports follow in a later rc per the per-class build-out roadmap.
[0.4.1rc13] - 2026-05-17¶
Task #248 — pi_cascade_digits cap expansion (engineering follow-on to PR #468 benchmark). Per user direction 2026-05-17 ("now I'm curious to know and think we should include in our notes, wall time to return 350 digit pi cascade, partly because it's a weird number on purpose"). The benchmark note in docs/srmech/notes/pi_cascade_digits_benchmark_2026-05-17.md surfaced the rc12 hard cap (num_digits ≤ 50 by validation, not by mathematics); rc13 closes that gap with auto-scaled cascade parameters + a 1000-digit ceiling.
Changed — pi_cascade_digits cap raised from 50 to 1000¶
srmech.amsc.rational._PI_CASCADE_MAX_DIGITS: 50 → 1000.srmech.amsc.rational._PI_CASCADE_MAX_DEPTH: 90 → 2000.- New constant
_PI_CASCADE_MAX_PRECISION_BITS = 32768(was hard-coded 8192 ceiling). pi_cascade_digits(num_digits, *, max_cascade_depth=None, precision_bits=None)— kwargs default toNone. WhenNone, the function auto-scales via the new_pi_cascade_auto_paramshelper. Existing rc12 callers (no kwargs / explicit defaults of 90 / 512) continue to work unchanged.- New helper
srmech.amsc.rational._pi_cascade_auto_params(num_digits) -> (depth, precision_bits)— linear scaling formula derived from the rc12 validated point:depth = max(90, ceil(num_digits * 90 / 50)),precision_bits = max(512, ceil(num_digits * 512 / 50)). Bit-exact pure-integer arithmetic, AST-clean (no math.pi access).
Changed — _integer_sqrt switched to math.isqrt (huge speedup)¶
- The rc12 implementation used a naive Newton iteration in pure Python.
math.isqrt(CPython 3.10+) implements an asymptotically-optimal Karatsuba-style integer-floor square root in C; at 20480-bit inputs (D=1000 cascade scale) the speedup is ~2500x. math.isqrtis NOT a transcendental constant access — the AST gate (which flagsmath.pi,math.tau,numpy.pi,np.pi,sympy.pi,scipy.pi) is unchanged and still passes.math.isqrtis pure-integer arithmetic, fully compatible with[[user_stance_pi_spectral_shape_scalar_invariant]]substrate-invariance discipline.- Without this optimization, num_digits=1000 would take ~24 minutes (extrapolated from naive Newton scaling). With
math.isqrt, num_digits=1000 takes ~0.7 seconds.
Added — 6 new rows in pi_digits/row.ndjson (12 total)¶
rc12's catalog at num_digits ∈ {5, 10, 15, 20, 25, 50} extended with rc13 cap-expansion rows at num_digits ∈ {100, 200, 350, 500, 750, 1000}. Each row cross-validated bit-exact against mpmath canonical π reference (the de-facto Python arbitrary-precision π implementation, via Borwein-Borwein 4th-order convergent algorithm). The 350-digit row is the user's "weird number on purpose" probe from PR #468.
Row schema's num_digits max widened: 50 → 1000.
Added — 8 new tests in tests/test_pi_cascade_primitives.py¶
test_pi_cascade_digits_350_weird_number_on_purpose— the deliberate probe valuetest_pi_cascade_digits_scaling_rc13[num_digits]— parametrised over {100, 200, 350, 500, 750, 1000}test_pi_cascade_digits_1000_rc13_ceiling— the new cap ceilingtest_pi_cascade_digits_over_rc13_cap_raises— cap validationtest_pi_cascade_digits_auto_params_helper— pins the auto-scaling formulatest_pi_cascade_digits_explicit_kwargs_override_auto— caller can overridetest_pi_cascade_digits_ast_no_math_pi_across_rc13_scale— AST gate survives cap expansion- Plus
_pi_cascade_auto_paramsadded to the AST-gate walk intest_pi_cascade_digits_call_graph_ast_no_math_pi
New canonical reference CANONICAL_PI_1000 in both test files (1000 decimal digits, cross-validated against mpmath).
Added — 3 new tests in tests/test_pi_digits_catalog.py¶
test_pi_digits_has_12_rows_rc13— pins row count at 12 (rc12's 6 + rc13's 6)test_pi_350_digits_canonical_weird_number_probe— regression-pinned 350-digit rowtest_pi_1000_digits_canonical_rc13_ceiling— regression-pinned 1000-digit row- Existing tests extended:
test_pi_digits_canonical_num_digits_valueswidened to all 12 levels,test_pi_cascade_digits_chain_falsification_all_rowsratchet bumped from 5 → 12,test_all_rows_share_canonical_pi_prefixreference widened from 50 → 1000 digits.
Wall time (Windows / Python 3.14.4 / fresh-venv)¶
| num_digits | depth (auto) | prec_bits (auto) | wall time |
|---|---|---|---|
| 50 | 90 | 512 | ~1 ms |
| 100 | 180 | 1024 | ~2 ms |
| 200 | 360 | 2048 | ~10 ms |
| 350 | 630 | 3584 | ~40 ms (the user's question) |
| 500 | 900 | 5120 | ~100 ms |
| 750 | 1350 | 7680 | ~310 ms |
| 1000 | 1800 | 10240 | ~700 ms |
The benchmark note's "seconds-range" projection for D=350 was based on the rc12 naive _integer_sqrt; the math.isqrt switch in rc13 collapses the projection from seconds to milliseconds.
Test count¶
- 535 → 549+ passed (full srmech suite; +14 new pi-related tests)
- tool_schema
pi_cascade_digitsToolEntry summary updated with rc13 cap-expansion note - JPL Rule 5 audit: no regression (no new C functions; the Python-only cap-expansion + math.isqrt swap don't touch the C surface)
- AST-verification gate: zero
math.piinvocations across the full call graph including the new_pi_cascade_auto_paramshelper
C parity¶
pi_cascade_digits stays Python-only per rc12's honest scope decision ([[feedback_no_binding_layer_carveout]]) — the cascade requires bignum integer arithmetic for precision_bits up to 32768 + the long-division step. rc13's expansion of the cap doesn't change that decision; if anything it reinforces it (the precision_bits requirements scale linearly with num_digits, and at 10240 bits the u64 envelope is comfortably exceeded). The Python wrapper around math.isqrt IS the C path here — CPython's math.isqrt is a C implementation in the interpreter itself.
continued_fraction_convergents (the companion Class N primitive shipped in rc12) retains its srmech_cf_convergents_int64 C surface unchanged.
Anchored in¶
[[user_stance_pi_spectral_shape_scalar_invariant]]— the convergent ladder IS π's substrate identity; the decimal expansion is downstream readout. rc13 cap-expansion makes more of the projection visible at the same substrate.[[user_stance_pi_as_projection]]— π is generated by the cascade-substrate operation[[feedback_every_doc_edit_faces_falsification]]— discipline this catalog operationalises[[feedback_no_binding_layer_carveout]]— Python-only by honest scope (bignum-required); not a binding-layer carve-out- Task #248 (this rc) — engineering follow-on to PR #468 benchmark
- PR #468 benchmark note (2026-05-17) — the engineering finding (rc12 caps at 50 by validation) + the scaling projection
- Spike #32 (PR #460) — empirical confirmation across 3 substrates
[0.4.1rc12] - 2026-05-16¶
Task #245 Milestone #4 — π geometric-cascade primitives (cascade output, no math.pi). Per user direction 2026-05-16 — operationalises [[user_stance_pi_spectral_shape_scalar_invariant]] (the convergent ladder IS π's substrate identity; the decimal expansion 3.14159... is downstream readout) via two new Class N primitives + a new chain-falsifiable pi_digits AMSC catalog. Confirms Spike #32 / PR #460 substrate-invariance result as on-disk falsification infrastructure (second instance of [[feedback_every_doc_edit_faces_falsification]] after asymptotic_calculus).
Added — 2 new Class N π geometric-cascade primitives (Python)¶
srmech.amsc.rational.continued_fraction_convergents(coef_list) -> list[tuple[int, int]]— produces the convergent ladder[(h_0, k_0), (h_1, k_1), ...]from a continued-fraction coefficient list via the standard CF recurrence (h_k = a_k * h_{k-1} + h_{k-2}). Canonical π CF[3; 7, 15, 1, 292, 1, ...]yields canonical convergents(3, 1), (22, 7), (333, 106), (355, 113), (103993, 33102), ...per Hardy & Wright §10.6. Pure-Python bignum-capable; C-standalone for int64-fit ladders viasrmech_cf_convergents_int64.srmech.amsc.rational.pi_cascade_digits(num_digits) -> str— streams decimal-digit expansion of π via Archimedes hexagon-doubling cascade. Uses integer Newton-Raphson rational √ at fixedprecision_bits(default 512) overmax_cascade_depthdoublings (default 90). Produces"3.14159..."as string. AST-verified zeromath.piinvocations anywhere in the call graph (discipline gate per[[user_stance_pi_spectral_shape_scalar_invariant]]).
Bounded caps: 256 CF coefficients (continued_fraction_convergents); 50 digits / depth 90 / 8192 bits (pi_cascade_digits). Substantially larger than any practical use case; both primitives are pure integer arithmetic throughout the call graph.
Added — C parity for continued_fraction_convergents¶
srmech_cf_convergents_int64(coefs, n, out_nums, out_dens)inc/src/srmech_rational.c+ header decl insrmech.h. ABI v2 pure-addition (no ABI bump). int64-bound n ≤ 256. ReturnsSRMECH_ERR_OVERFLOWwhen any convergent exceeds int64; Python wrapper falls through to bignum. Two helper functions (cf_conv_sadd_i64,cf_conv_step) split per JPL Rule 4 (≤60 LOC) with ≥2 asserts per non-exempt function (Rule 5).
pi_cascade_digits stays Python-only — the cascade requires bignum integer arithmetic for precision_bits ≥ 512 + the long-division step, neither of which fits the JPL-clean u64 envelope of the C primitive surface. Honest scope decision per [[feedback_no_binding_layer_carveout]]: every primitive class earns a C surface; bignum-required cases stay Python.
Added — srmech.amsc.attested.pi_digits/ chain-falsifiable catalog¶
First chain-falsifiable π substrate-invariance catalog. Operationalises Spike #32 / PR #460 result (substrate-invariance across triangle / square / hexagon cascades with AST-verified zero math.pi invocations).
descriptor.toml— single-steppi_cascade_digitschain calling the Class N primitive.row.ndjson— 6 self-validating rows at canonical precision levels: num_digits ∈ {5, 10, 15, 20, 25, 50}. Each row'sexpected_pi_stringis the bit-exact canonical decimal expansion of π verifiable against Khinchin Continued Fractions §10.row.schema.json— JSON schema for the row data (enforcesexpected_pi_stringstarts with"3.").
Mathematical anchor: π's substrate identity is the cascade-emergent CF convergent ladder per [[user_stance_pi_spectral_shape_scalar_invariant]]; the decimal expansion is a downstream readout under continuous-length-metric projection. Catalog rows are the readable artifact backed by the cascade primitive's substrate computation. Source citations: Khinchin Continued Fractions §10 (canonical π CF); Hardy & Wright Theory of Numbers §10.6 (best-rational convergent property); Archimedes Measurement of a Circle c. 250 BCE (hexagon-doubling cascade algorithm).
Added — tests/test_pi_cascade_primitives.py (27 tests, all green)¶
- Continued-fraction convergents: canonical π convergents (first 6 + full 16), canonical e convergents cross-check, simple-CF edge cases, bignum ladder, input validation
- pi_cascade_digits: 0/5/10/15/20/25/50 digit canonical values, prefix consistency check, input validation, low-depth divergence behavior, default-kwargs consistency
- AST-verification gate: three discipline tests confirming zero
math.pi/numpy.pi/math.tau(or equivalent) attribute accesses acrosspi_cascade_digits, its private helpers (_integer_sqrt,_scaled_integer_sqrt), andcontinued_fraction_convergents - Substrate-readout consistency: 355/113 convergent agrees with
pi_cascade_digits(6)first 6 digits
Added — tests/test_pi_digits_catalog.py (9 tests, all green)¶
- Catalog presence + ≥5 rows
- Canonical num_digits coverage (5, 10, 15, 20, 25, 50)
- Chain falsification (bit-exact comparison row-by-row)
- Canonical regression-pinned values (15-digit IEEE-754 boundary, 50-digit bignum-deep)
- All rows are prefixes of canonical π (substrate-invariance documentation)
- Attestation field presence per descriptor's
[attestation]block
Test count¶
- 499 → 535 passed (full srmech suite; +36 new tests)
- tool_schema ToolEntry coverage bumps by 2 (one per new Class N primitive)
- JPL Rule 5 audit: no regression (new C helpers cf_conv_sadd_i64 + cf_conv_step + srmech_cf_convergents_int64 each have ≥ 2 asserts)
- JPL Rule 4: every new C function ≤ 60 lines (cf_conv_step is 16 LOC; srmech_cf_convergents_int64 is 28 LOC)
Numbering note¶
rc12 is Milestone #4 closing Task #245 — the π substrate-output primitive shipping. Predecessor rc11 (Milestone #2 Phase 3B) closed transcendental-Taylor inventory. Following rcs continue Task #234 §11 (forward_difference / riemann_sum), Task #218 Phase C2 work, or new milestones per user direction.
Anchored in¶
[[user_stance_pi_spectral_shape_scalar_invariant]]— the convergent ladder IS π's substrate identity (this catalog operationalises this stance)[[user_stance_pi_as_projection]]— older form; ladder vs. decimal is the projection-shadow boundary[[user_stance_identity_not_implementation_discipline]]— umbrella discipline (π IS the ladder at substrate level; π HAS a decimal expansion at notation level)[[feedback_every_doc_edit_faces_falsification]]— discipline this catalog operationalises (second concrete instance)[[feedback_no_binding_layer_carveout]]— C surface for continued_fraction_convergents (int64 path); Python-only for pi_cascade_digits (bignum required) is honest scope, not binding-layer carve-out- Spike #32 (PR #460) — empirical confirmation across 3 substrates with AST-verified zero math.pi
[0.4.1rc11] - 2026-05-16¶
Task #234 Phase 3B — trig + log Taylor primitives (Milestone #2 second ship). Per user direction 2026-05-16 ("number 2 and 3 in the same milestone, do sequentially and test with testpypi first"). Phase 3A shipped cosmos_validation catalog as rc10; Phase 3B adds 4 trig/log Taylor partial-sum primitives + chain specs + rows to the asymptotic_calculus catalog. Closes Task #234 §11 inventory's transcendental row.
Added — 4 new Class N Taylor-series primitives (Python)¶
srmech.amsc.rational.sin_series_truncate(num, den, num_terms) -> (out_num, out_den)— sin(p/q) = Σ (-1)^k (p/q)^(2k+1) / (2k+1)!srmech.amsc.rational.cos_series_truncate(num, den, num_terms) -> (out_num, out_den)— cos(p/q) = Σ (-1)^k (p/q)^(2k) / (2k)!srmech.amsc.rational.log1p_series_truncate(num, den, num_terms) -> (out_num, out_den)— log(1+p/q) = Σ_{k=1} (-1)^(k+1) (p/q)^k / k (caller responsibility: |p/q| < 1 for convergence)srmech.amsc.rational.atan_series_truncate(num, den, num_terms) -> (out_num, out_den)— atan(p/q) = Σ (-1)^k (p/q)^(2k+1) / (2k+1) (caller responsibility: |p/q| ≤ 1)
Pure Python bignum-capable; uses common-denominator integer accumulation with periodic gcd reduction. Bounded num_terms (50 for trig; 64 for log/atan) so the per-row time stays acceptable.
Deferred — C parity for trig primitives (rc12 candidate)¶
Per [[feedback_no_binding_layer_carveout]] every Class N op earns a C surface. rc11 ships Python-only because the 4 trig primitives use bignum-accumulating Taylor series whose intermediates exceed u64 for typical (x, N) inputs; the C path would need either (a) tight num_terms bounds and OVERFLOW returns on most catalog rows, or (b) multi-precision integer infrastructure in C. Honest scope decision: ship Python-only at rc11; add C parity in rc12 with a documented narrow-bound case (matching what srmech_exp_series_truncate already does at u64 limits). Tracked as a follow-on task. The rc8 exp_series_truncate C surface remains the canonical example of C-standalone discipline; rc11 adds 4 surfaces to the same C parity work queue.
Added — asymptotic_calculus/descriptor.toml — 4 new chain specs¶
sin_series_truncatechain — single Class N stepcos_series_truncatechainlog1p_series_truncatechainatan_series_truncatechain
Each chain takes (@row.x_num, @row.x_den, @row.num_terms) and returns (out_num, out_den) exact rational. Row schema's kind enum widened from ["exp"] to ["exp", "sin", "cos", "log1p", "atan"].
Added — 16 new self-validating rows in row.ndjson¶
Per-op rows covering canonical inputs: - sin: x=0, ⅙ (~30°), ¼ (~14°), 1 rad - cos: x=0, ⅙, ¼, 1 rad - log1p: x=0, 1/10, ¼, -¼ - atan: x=0, ¼, ½, 1 (~π/4)
Each row's (expected_num, expected_den) computed bit-exactly by running the new Python op at catalog-author time.
Test count¶
- 482 → 484 passed (full srmech suite);
tests/test_asymptotic_calculus_catalog.py::test_exp_series_truncate_chain_falsification_all_rowsexpanded to dispatch bykindand bit-exact-compare each row across all 5 chain types - tool_schema ToolEntry coverage ratchet bumps by 4
Numbering note¶
This is the second rc in Milestone #2. Phase 3A (cosmos_validation catalog, rc10) shipped first per user direction "do sequentially". rc11 closes the trig-primitive portion of Task #234 §11. Future rc12 ships C parity for the 4 trig primitives + adds calculus operators (forward_difference, riemann_sum) per Task #234 §11 inventory.
Anchored in¶
[[feedback_every_doc_edit_faces_falsification]]— discipline- Task #234 §11 inventory — scope (sin / cos / tan / log / atan / sinh / cosh / Bessel / Γ / ζ / forward_difference / riemann_sum)
- User direction 2026-05-16 Milestone #2 — "do sequentially and test with testpypi first"
[0.4.1rc10] - 2026-05-16¶
Task #234 Phase 3A — cosmos_validation catalog ship (Spike #27 / PR #437 Q6.1 falsification infrastructure). Per user direction 2026-05-16 (Milestone #2: "Task #234 — asymptotic_calculus expansion: cosmos_validation + trig primitives"). First instance of the milestone's pattern: a chain-falsifiable cosmology catalog using only existing Class N rational primitives plus 4 new rational arithmetic ops with Python + C parity.
Added — Class N rational arithmetic primitives (4 new ops with full C/Python parity)¶
srmech.amsc.rational.rational_add(a, b) -> (num, den)— add two rationals, reduced.srmech.amsc.rational.rational_mul(a, b) -> (num, den)— multiply two rationals, reduced.srmech.amsc.rational.rational_div(a, b) -> (num, den)— divide two rationals, reduced; raises ZeroDivisionError on b_num=0.srmech.amsc.rational.rational_pow_uint(base, exp) -> (num, den)— raise rational to non-negative integer exponent; exp ≤ 64.
All four take tuple inputs (p, q) for clean chain composition via Phase 2 v1 list-resolution in compose._resolve_args. C surfaces: srmech_rational_add / srmech_rational_mul / srmech_rational_div / srmech_rational_pow_uint in c/src/srmech_rational.c + header decls in srmech.h + ctypes bindings in _native.py. Each Python wrapper dispatches to C when inputs fit u64; falls through to bignum on OVERFLOW or missing-symbol. Per [[feedback_no_binding_layer_carveout]] the C library is usable standalone for u64-fit inputs. JPL Power-of-Ten clean: helpers split per Rule 4 (≤60 LOC), ≥2 asserts per function (Rule 5).
Added — srmech.amsc.attested.cosmos_validation/ catalog¶
First chain-falsifiable cosmology catalog. Operationalises Spike #27 / PR #437 Q6.1 dark-sector monotonicity claim (concertmaster's PR #437 audit recommendation 1).
descriptor.toml— 9-stepfriedmann_dark_fractionchain composing rational_pow_uint (1) + rational_mul (3) + rational_add (4) + rational_div (1).row.ndjson— 11 self-validating rows: Planck-canonical Ω values (Ω_b = 49/1000, Ω_c = 265/1000, Ω_Λ = 685/1000, Ω_r = 1/10000) + scale-factor a across z ∈ [-0.9, ~10⁵]. Each row stores expectedf_dark(a)rational; CI runs the chain and bit-exact-compares.row.schema.json— JSON schema for the row data.
Mathematical claim: f_dark(a) = (Ω_c·a + Ω_Λ·a⁴) / (Ω_b·a + Ω_c·a + Ω_Λ·a⁴ + Ω_r). Q6.1 monotonicity: f_dark(a) strictly increases in a — verified bit-exact across the 11 rows (0.026 at a=1/100000 → 0.951 at a=1 → 0.99993 at a=10). Source: Planck Collaboration 2018 VI (Aghanim et al. 2020, A&A 641:A6, doi:10.1051/0004-6361/201833910, arXiv:1807.06209) per [[feedback_pdf_extraction_citation_discipline]].
Added — tests/test_cosmos_validation_catalog.py (9 tests, all green)¶
- Catalog presence + row count
- Chain bit-exact falsification (all 11 rows)
- Q6.1 monotonicity test: sorts rows by a, asserts strict-increase across all consecutive pairs
- Unit tests for each new rational op
- Canonical pin: f_dark(a=1) = 9500/9991
Added — tool_schema entries for the 4 new rational ops¶
Coverage ratchet bumps; each op cites Class N's rational-approximation primitive role.
Numbering note¶
This rc10 builds on the rc1-rc9 sprint that shipped in 0.4.1rc9 (merged to main via PR #447). It is the first rc in Milestone #2 (Task #234 — asymptotic_calculus expansion). Subsequent rc11 will add sin/cos/log/atan trig primitives (Phase 3B per user direction "do sequentially and test with TestPyPI first").
Anchored in¶
[[feedback_every_doc_edit_faces_falsification]]— discipline this catalog operationalises[[feedback_no_binding_layer_carveout]]— every new Class N op gets a C surface[[user_stance_pi_as_projection]]+[[user_stance_kepler_shape_universal]]+[[user_stance_asymptotic_dof_sidesteps_infinity]]+[[user_stance_epicycle_via_gear_plus_pin]]— the stance family- Spike #27 / PR #437 Q6.1 monotonicity claim (concertmaster audit reproduced 9999/9999 positive slopes; this catalog ships the analytic proof as 11 bit-exact rows)
[0.4.1rc9] - 2026-05-16¶
Hotfix: asymptotic_calculus row attestation field. The rc8 fresh-venv TestPyPI smoke surfaced that catalog.get_attested_dataset("asymptotic_calculus") failed at row-parse time because the literature_curated adapter requires every row to carry a source_published_date field for per-row attestation. The rc8 row.ndjson had source_apostol + source_bishop references but not the explicit publication date.
Fixed¶
- All 12 rows in
srmech.amsc.attested.asymptotic_calculus/row.ndjsonnow carrysource_published_date = "1974-01-01"(Apostol Mathematical Analysis 2nd ed. publication; the citation pinned for the convergence claim per Theorem 12.20). srmech.amsc.attested.asymptotic_calculus/row.schema.jsonaddssource_published_dateto its required-fields list + properties (ISO 8601 date format).
Behaviour impact¶
bridge.get_attested_dataset("asymptotic_calculus", limit=N)returns rows cleanly.bridge.attestation_audit("asymptotic_calculus")resolves per-row attestation hashes.- Python
srmech.amsc.rational.exp_series_truncate(...)and the C pathsrmech_exp_series_truncate(...)unchanged from rc8.
This is a 12-row + 1-schema-line patch; no C code or Python primitive changes.
[0.4.1rc8] - 2026-05-16¶
Spike #28 ship — asymptotic_calculus catalog + Class N exp_series_truncate with C parity (re-versioned from rc6). Originally drafted as rc6 on PR #447's branch; renumbered to rc8 after PR #439's rc7 chain-spec hotfix merged to main between the two PRs. The underlying ship is identical (asymptotic_calculus catalog, exp_series_truncate op, math addendum + chain-spec form + scope inventory) plus the C parity surface that was deferred at rc6 ship and now lands on top per [[feedback_no_binding_layer_carveout]] — the C library is usable standalone, no Python required.
Added — Class N op exp_series_truncate with full C/Python parity¶
- Python
srmech.amsc.rational.exp_series_truncate(numerator, denominator, num_terms) -> (out_num, out_den)— computes the exp Taylor partial sumS_N(p/q) = sum_{k=0..N} (p/q)^k / k!as an exact rational in lowest terms. Composes: - Class N rational-approximation: numerator/denominator tracking + gcd reduction
- Class J integer factorial:
k!as running integer product - Class I integer arithmetic: power accumulators
p^k,q^k - Pure integer arithmetic at every step; arbitrary-precision via Python int for N ≤ 512.
- C
srmech_exp_series_truncate(int64_t x_num, uint64_t x_den, uint32_t num_terms, int64_t *out_num, uint64_t *out_den) -> srmech_status_t— same op as the Python surface, bounded tonum_terms ≤ 20(factorial fits u64); returnsSRMECH_ERR_OVERFLOWwhen intermediate computation would exceed u64 range. The Python wrapper dispatches to C when inputs fit safe bounds, falls back to bignum Python when they don't. The C library compiles and runs standalone — no Python interpreter required for the catalog-row-shaped inputs (x ∈ {0, ±½, ±1, ±2}, N ∈ {5, 10, 15} all fit u64 comfortably). - Canonical SSoT: Apostol Mathematical Analysis 2nd ed. Theorem 12.20 (Lagrange remainder); Bishop Foundations of Constructive Analysis §2 (asymptotic-rate framing). Both cited per
[[feedback_pdf_extraction_citation_discipline]].
Added — srmech.amsc.attested.asymptotic_calculus/ catalog¶
First concrete instance of the doc-claim falsification infrastructure (per [[feedback_every_doc_edit_faces_falsification]]):
descriptor.toml— single-step Phase 2 v1 chain specexp_series_truncatecalling Class Nexp_series_truncateop with@row.x_num,@row.x_den,@row.num_termsinputs.row.ndjson— 12 self-validating rows: x in {0, ±½, ±1, ±2} at N in {5, 10, 15}; each row carries (x_num, x_den, num_terms) input plus (expected_num, expected_den) bit-exact ground-truth output.row.schema.json— JSON schema for the row data.
Future rcs (Task #234) expand the catalog to cover sin, cos, tan, log, atan, Bessel, Γ, ζ partial sums + calculus operations (forward-difference, Riemann sum, continued-fraction convergent). Each new operation lands as a new Class N op (Python + C surface) + new chain spec + new row data.
Added — tests/test_asymptotic_calculus_catalog.py (7 tests, all green)¶
The falsification test runs the chain for every catalog row and bit-exact compares the produced output to the row's stored expected output. Any drift in Class N + Class J primitives surfaces as immediate row-by-row test failure. Includes regression-pinned canonical exemplars: S_10(1) = 9864101/3628800 (Spike #28 §9 V4 canonical exemplar) + S_N(0) = 1/1 (trivial-input pin). Plus C/Python parity test (new at rc8): C path matches Python path bit-exact for num_terms ≤ 20 inputs.
Added — tool_schema coverage for exp_series_truncate¶
ToolEntry registered in srmech.amsc.tool_schema under category="rational"; coverage ratchet bumps from previous floor.
Numbering note¶
- rc6 — drafted on PR #447's branch (commit
8887368historical); renumbered to rc8 at rebase time. Not tagged on TestPyPI. - rc7 — PR #439's chain-spec hotfix (merged to main). Tagged + published; removes 4 Phase 1 worked-example chains from cosmos catalogs (see rc7 entry below).
- rc8 — this rebase carries PR #447's content forward + adds C parity for
exp_series_truncateper[[feedback_no_binding_layer_carveout]].
Anchored in¶
- Spike #28 working note:
docs/antikythera-maths/research-mfo/asymptotic_vs_infinity_history_2026-05-16.md— §9 falsification math (V1-V4), §10 canonical chain-spec form, §11 catalog scope inventory [[feedback_every_doc_edit_faces_falsification]]— discipline this catalog operationalises[[feedback_no_binding_layer_carveout]]— C-standalone contract honoured by rc8's C surface[[user_stance_pi_as_projection]]+[[user_stance_kepler_shape_universal]]+[[user_stance_asymptotic_dof_sidesteps_infinity]]+[[user_stance_epicycle_via_gear_plus_pin]]— the upstream stance family this catalog instantiates operationally
[0.4.1rc7] - 2026-05-16¶
Spike #28 ship — falsification-discipline pre-merge hotfix. Removes the four Phase 1 worked-example chain specs from the cosmos catalogs because they reference primitives that don't yet exist (their chains list cleanly but fail at activate-time when actually run). Per [[feedback_every_doc_edit_faces_falsification]] (user direction 2026-05-16: "our model has not lied to us yet, so I believe the math still") we do not ship chain specs whose underlying primitives can't run — chains that cannot execute are claims that cannot falsify. Surfaced via the rc5 fresh-venv TestPyPI smoke (Class D match_filter, Class E sorted_lookup_extract + sorted_lookup_batch, Class L spherical_harmonic_decompose + extract_preferred_axis, Class I angular_separation_axes all missing). Each chain re-lands as its underlying primitive ships — spherical_harmonic_decompose is Spike #26 Phase 2 scope (Task #227); angular_separation_axes is Task #234 §11 inventory (cmb_angular_geometry sister catalog with rc7+ sin/cos). The DSL design pattern lives in docs/srmech/adr/0002-phase-1-operator-chain-schema.md for reference.
Changed — cosmos catalog chain specs removed¶
srmech/amsc/attested/cmb_low_ell_maps/descriptor.toml: removedmultipole_vector_axis(LLDA) andt_vs_e_axis_differential(LLLLI) chain specs.srmech/amsc/attested/cmb_polarisation_spectra/descriptor.toml: removedacoustic_peak_locations(CDE) chain spec.srmech/amsc/attested/cmb_bispectrum/descriptor.toml: removedf_NL_template_combination(ENA) chain spec.- All three descriptors retain
[catalog].chain_schema_version = 1so re-adding chains later does not require reintroducing the[catalog]section. Inline deprecation comments document what was removed and which Task # tracks the re-add.
Behaviour impact¶
srmech.amsc.catalog.list_catalog_chains(<cosmos_catalog>)now returns{"ok": True, "source_key": ..., "n_chains": 0, "chains": []}for each of the three affected catalogs. Pre-rc7 it returnedn_chains=1orn_chains=2butrun_catalog_chainwould then raiseChainSpecErrorat activate time. The rc7 behaviour is strictly more honest: empty chain list reflects empty executable surface.- All other rc5 functionality intact: cosmos catalog data rows + chain schema infrastructure + composition engine + Class L broadening + tool_schema coverage.
cmb_lensingcatalog never had chain specs and is unaffected.
Test¶
test_compose.test_parse_catalog_chains_cosmos_descriptors_have_no_executable_chains pins n_chains == 0 across all 3 cosmos catalogs at rc7.
[0.4.1rc5] - 2026-05-16¶
ADR-0002 Phase 2 — Class L broadening + composition engine + notebook updates. Implements the Phase 1 spike's dissolve-into-Class-L proposal per [[feedback_no_privileged_primitive_classes]]. Class L's identity broadens from "graph Laplacian" to "dense-matrix linear algebra including eigendecomposition + matrix-vector multiplication + elementwise operations"; the graph-Laplacian-specific ops become specialisations. Adds the operator-chain composition engine (srmech.amsc.compose) implementing schema v1 from the Phase 1 ADR doc, with linear pipeline execution + 4-namespace reference DSL (@row.* / @input.* / @step[N].output / @catalog.*) + chain-level and per-step error policy. Engine integration with the catalog bridge: list_catalog_chains(source_key) and run_catalog_chain(source_key, chain_name, row_index, inputs). Vocabulary stays at 14 classes A–N; no Class P promoted.
Added — Class L broadening (4 new ops, full C + Python parity)¶
Each new op cites canonical physics literature per [[feedback_science_is_ssot_not_project]]:
srmech.amsc.laplacian.hermitian_eigendecompose(H) -> (eigvals, V)— complex Hermitian generalisation ofjacobi_eigvals. Returns ascending eigenvalues + unitary eigenvectors. Native C path viasrmech_hermitian_eigendecompose(complex-Jacobi rotations with algebraic phase factore^(iφ) = γ/|γ|; pi-free, atan2-free, n ≤ 256). Numpy fallback vianp.linalg.eigh. Canonical SSoT: Golub & Van Loan Matrix Computations (4th ed., 2013) §8.5.srmech.amsc.laplacian.dense_matvec_complex(M, v) -> M @ v— general complex matrix-vector multiplication. Native C path viasrmech_dense_matvec_complex; numpy fallback. Canonical SSoT: Golub & Van Loan §1.1.srmech.amsc.laplacian.elementwise_multiply_complex(a, b) -> a * b— vectorised pointwise complex multiply with broadcasting. Native C path; numpy fallback.srmech.amsc.laplacian.elementwise_transcendental(arr, op_name)forop_name ∈ {"exp", "cos", "sin", "log", "exp_i"}. Array-vectorised transcendentals over real input;exp_i(x) = exp(1j * x)(TDSE-relevant complex exponential) realised in Python ascos + i*sinover the real argument via two C calls. Canonical SSoT: ANSI C99 §7.12 libm.
The LAPLACIAN_OPS module-level constant exposes all 8 op names (4 original + 4 new) for the composition-engine registry.
Added — composition engine (srmech.amsc.compose)¶
ChainSpecdataclass mirroring the TOML[[catalog.operator_chain]]schema.StepSpecdataclass mirroring[[catalog.operator_chain.steps]]entries.parse_chain_spec(chain_dict)— schema-v1 validation; rejects malformed reference syntax, unknown class identifiers, out-of-bounds@step[N]references, illegalon_errorvalues, empty step lists.parse_catalog_chains(toml_dict)— parses all chains in a descriptor TOML; requires[catalog].chain_schema_version = 1.resolve_chain(spec, registry)— binds each step'sclass.opagainstDEFAULT_CLASS_REGISTRY(covers all 14 classes A–N); raisesChainSpecErroron missing op at activation time.run_chain(spec, *, row, inputs, registry)— top-level executor; linear pipeline; error policy (raise / warn_return_none / skip-NYI-for-single-call).- 4-namespace reference DSL resolution at runtime:
@row.<path>,@input.<name>,@step[N].output[.<path>],@catalog.<row_key>.<col>.
Added — catalog bridge integration¶
srmech.amsc.catalog.list_catalog_chains(source_key)returns{ok, source_key, n_chains, chains}where each chain has{name, summary, returns, on_error, n_steps, classes}. ADR-0002 Phase 2 bridge surface.srmech.amsc.catalog.run_catalog_chain(source_key, chain_name, *, row_index, inputs)executes the named chain with optional row binding. Phase 1's 4 worked-example chains across 3 cosmos catalogs are now invocable via this bridge.
Added — C surface¶
srmech_hermitian_eigendecompose(n, H_il, eigvals, V_il)— complex Hermitian eigendecomposition via complex-Jacobi rotations. Pi-free, atan2-free; complex numbers travel as interleaved-double pairs (re, im, re, im) on the FFI boundary. Bounded bySRMECH_LAPLACIAN_MAX_NODES= 256.srmech_dense_matvec_complex(rows, cols, M_il, v_il, out_il)— complex matvec.srmech_elementwise_multiply_complex(n, a_il, b_il, out_il)— pointwise complex multiply.srmech_elementwise_transcendental(n, arr, op_id, out)— real transcendental dispatcher; op_id enumSRMECH_TRANS_{EXP,COS,SIN,LOG}insrmech.h.
ABI version stays at v2 — additive symbol additions don't break the wire contract. JPL Power-of-Ten audit clean: each new function ≤ 60 lines, ≥ 2 assertions, no goto, no malloc, no unbounded loops.
Added — tool-schema entries (10 new entries)¶
srmech.amsc.laplacian.{hermitian_eigendecompose, dense_matvec_complex, elementwise_multiply_complex, elementwise_transcendental}— Class L broadening.srmech.amsc.catalog.{list_catalog_chains, run_catalog_chain}— bridge surfaces.srmech.amsc.compose.{parse_chain_spec, parse_catalog_chains, resolve_chain, run_chain}— engine surfaces.
Tool-schema coverage ratchet (tests/test_tool_schema_coverage.py) continues green.
Added — tests (39 new test cases)¶
tests/test_laplacian_class_l_broadening.py(18 tests): parity with numpy for all 4 new ops; Hermitian eigendecomposition convergence + unitarity + 2×2 Pauli-Y reference;LAPLACIAN_OPSregistry coverage; end-to-end TDSE composition test (hermitian_eigendecompose→dense_matvec_complex→elementwise_transcendental("exp_i")→elementwise_multiply_complex→dense_matvec_complex) verified against reference path to 1e-10 with norm preservation.tests/test_compose.py(21 tests): schema validation; reference DSL namespace resolution; linear pipeline threading; error policy; catalog-level chain parsing including the 4 real Phase 1 cosmos chains end-to-end.
Full suite: 547 passed (508 pre-Phase-2 + 39 new).
Documentation¶
docs/srmech/srmech_research_notebook.md§3.8.3 added — Class L broadening rationale, the 4 new ops with canonical SSoT citations, dissolve-vs-promote framing per[[feedback_no_privileged_primitive_classes]]. Cross-references to ADR-0002 Phase 1 schema doc and Phase 1 report.docs/antikythera-maths/mfo_spectral_research_notebook.md§VIII.6.1 — added "Closure-validation observation #2 — ADR-0002 Phase 1 TDSE spike" paragraph noting the second affirmative closure-validation (after Phase C1's QM/QFT/SM ops layer landing without new primitives). The closure conjecture (14 primitives suffice) now stands at two independent positive verifications.docs/srmech/python/CHANGELOG.md— this entry.
Notes¶
- Per
[[feedback_no_binding_layer_carveout]]: Class L's broadening earns its full C surface (4 new symbols + ctypes bindings), not Python-only. - Per
[[feedback_no_mvp_framing]]: rc5 covers the full Phase 2 surface (Class L broadening + composition engine + catalog integration + tests + notebook updates), not a Phase-2a-then-Phase-2b carve-out. - Per
[[feedback_rc_stacking_versioning]]: rc5 stacks on the active 0.4.1 cosmos-catalog sprint onfeat/srmech-cosmos-catalog; clean 0.4.1 ships when sprint concludes. - Phase 2 open questions (branching / chain-level iteration / cross-source reduction / auto-derived tool-schema parameter types / versioned op evolution) remain Phase 2-v2 scope per Phase 1 §11.
[0.4.1rc4] - 2026-05-16¶
ADR-0002 Phase 1 — operator-chain DSL design + worked-example specs + spike. Formalises the descriptor TOML operator-chain DSL sketched in ADR-0002 §3. Schema v1 candidate lands as a new ADR Phase 1 document; four worked-example chains land across three of the four cosmos catalogs (cmb_low_ell_maps × 2, cmb_polarisation_spectra × 1, cmb_bispectrum × 1); the spike — closed-form TDSE evolution from srmech.qm.single_particle.tdse_evolve — surfaces a Class L scope-broadening question with a clean dissolve-into-existing-class proposal per [[feedback_no_privileged_primitive_classes]]. The vocabulary stays at 14 classes A–N; no new primitive class promoted.
Added — schema v1 documentation¶
docs/srmech/adr/0002-phase-1-operator-chain-schema.md(~330 lines): formalised schema specification resolving 7 design concerns from the conductor's Phase 1 brief.- Step shape:
class+op+args(+ optional per-stepon_error). Closed shape. - Data flow: linear pipeline with explicit
@step[N].outputreferences. No implicit threading. No DAG / branching in v1. - Input binding: reference DSL with four namespaces (
@row.X,@input.X,@step[N].output,@catalog.<key>.<col>). - Return shape: typed string
"<type> # <comment>"parseable viatypingutilities. - Error policy: default
raise; opt-inwarn_return_none/skip. - Versioning: required
[catalog].chain_schema_version = 1when chains declared. - Reference DSL grammar formalised; engine validates at chain activation.
- Includes a JSON Schema (
srmech.amsc.operator_chain.v1) for descriptor validation pipelines.
Added — four worked-example chains¶
| Catalog | Chain | Classes | Steps | Purpose |
|---|---|---|---|---|
cmb_low_ell_maps |
multipole_vector_axis |
L + L + D + A | 4 | de Oliveira-Costa 2004 §III axis extraction at fixed ℓ |
cmb_low_ell_maps |
t_vs_e_axis_differential |
L + L + L + L + I | 5 | §VII.6.3.1 falsifiable Δθ_TE prediction (predicted 1.0°–2.0°; threshold < 0.1°) |
cmb_polarisation_spectra |
acoustic_peak_locations |
C + D + E | 3 | TT/TE/EE peak enumeration via NDJSON stream + multi-needle dispatch + sorted extract |
cmb_bispectrum |
f_NL_template_combination |
E + N + A | 3 | Joint rational-form bound across the 3 primordial bispectrum templates |
All four chains parse cleanly via python -m tomllib; canonical SSoT citations per chain (Planck 2018 IV / V / IX) all PDF-extraction-verified per [[feedback_pdf_extraction_citation_discipline]].
TOML-syntax note: the original ADR-0002 §3 sketch used multi-line inline-table arrays (steps = [ { ... }, { ... } ]) which the TOML spec forbids. The Phase 1 canonical form lifts each step to its own [[catalog.operator_chain.steps]] array-of-tables entry; same semantic content, valid TOML, tomllib-round-tripped. The schema doc §2 documents the correction.
Spike — closed-form TDSE evolution surfaces Class L scope question¶
The spike calculation srmech.qm.single_particle.tdse_evolve(H, ψ, t) = V·diag(exp(-iλt))·V^H·ψ (Sakurai §2.1.5 eq 2.1.40) decomposes to 5 conceptual steps: Hermitian eigendecompose + change-of-basis ψ→eigenbasis + elementwise exp(-iλt) + elementwise multiply + change-of-basis back. Step 0 fits Class L (with complex-Hermitian generalisation of existing real-symmetric jacobi_eigvals); steps 1, 3, 4 (complex matvec, elementwise multiply) and step 2 (elementwise transcendental over complex array) do NOT cleanly fit any existing A–N class op.
Proposed Phase 2 refinement (per [[feedback_no_privileged_primitive_classes]] dissolve-before-promote):
broaden Class L's identity from "graph Laplacian" to "dense-matrix linear algebra including eigendecomposition + matvec + elementwise operations". New Class L ops in Phase 2:
- hermitian_eigendecompose(H) — complex-Hermitian generalisation
- dense_matvec_complex(M, v) — general complex matvec
- elementwise_multiply_complex(a, b) — vectorised pointwise
- elementwise_transcendental(arr, op_name) — array-vectorised exp/cos/sin/etc.
Class L's existing graph-Laplacian-specific ops (dense_laplacian, normalized_laplacian) become specialisations of the broader dense-matrix scope. No new primitive class promoted; vocabulary stays at 14 classes A–N.
Added — Phase 1 report¶
docs/srmech/notes/adr_0002_phase_1_dsl_design_2026-05-16.md(~290 lines): consolidated design decisions + worked-example overview + spike write-up + open questions for Phase 2.
No code change; no C ABI change; no Python API change¶
- C ABI v2 unchanged.
- No new C symbols. No JPL audit pin changes.
- No
srmech.amsc.<class>Python surface changes. - No
srmech.qm.*operation changes. - Schema is data-only addition to descriptor.toml; no Python composition-engine code yet (Phase 2 scope).
Versioning¶
0.4.1rc3 → 0.4.1rc4. Sprint-level rc-stacking per [[feedback_rc_stacking_versioning]], not a separate ship. Cumulative cosmos catalog sprint accumulates: rc1 (3-catalog data layer + framework precedent) + rc2 (read_ndjson framework fix) + rc3 (cmb_low_ell_maps catalog #4) + rc4 (this — ADR-0002 Phase 1 schema + 4 chains + spike). Clean 0.4.1 ships when sprint accumulates everything the cosmos catalog research thread + ADR-0002 Phase 1 implementation prep needs.
[0.4.1rc3] - 2026-05-16¶
Cosmos catalog extension — Spike #26 Phase 1 data layer folded into the 0.4.1 sprint. Adds the fourth srmech-primary cosmos catalog source, cmb_low_ell_maps, providing metadata for Planck PR3 component-separated full-sky CMB maps (Commander / NILC / SEVEM / SMICA) + common-mask products. Phase 2 (the analysis script) will fetch the FITS bytes via the catalog URLs and compute T-mode + E-mode a_ℓm coefficients for multipole-vector AoE-direction extraction; the framework prediction Δθ_TE ≈ 1°–2° from §VII.6.3.1's 138°/unit-f_RD bundle-projection-reconfiguration rate × Δf_RD across the T-vs-E recombination visibility window will be tested against observation.
Added — cmb_low_ell_maps attested source¶
7 rows of provenance metadata (4 sky-map FITS + 3 mask products) at srmech/amsc/attested/cmb_low_ell_maps/. FITS bytes are not committed (each map is ~168 MB; 672 MB total exceeds git's reasonable storage envelope); Phase 2 fetches via the per-row source_url field from PLA's HTTP CDN (pla.esac.esa.int/pla/aio/product-action?MAP.MAP_ID=...) which serves Planck data per ESA's Open Access policy without authentication.
Canonical citations (PDF-extraction verified per [[feedback_pdf_extraction_citation_discipline]]):
- Planck 2018 IV (diffuse component separation): arXiv:1807.06208, A&A 641 A4
- Planck 2018 VII (isotropy + statistics): arXiv:1906.02552, A&A 641 A7
Placement decision (rc3 only — not a framework change)¶
Per user directive on the MFO/AoE research line — "MFO and srmech ship as one, because it demonstrates every class operator" — the new cmb_low_ell_maps catalog is placed in srmech (srmech/amsc/attested/), matching the rc1 cosmos catalog precedent (cmb_polarisation_spectra + cmb_bispectrum + cmb_lensing). The Spike #26 Phase 1 concertmaster initially placed the catalog in ephemerides-spectral for cmb-family co-location; reworked here to align with rc1's placement and the user's "MFO + srmech ship as one" directive. Future migration of all cosmos catalogs to ephemerides-spectral remains the eventual plan once MFO matures and earns its own scope.
Companion research note (separate path)¶
Phase 2 scope artifact at docs/antikythera-maths/research-mfo/vii_6_3_1_prediction_verification_scope_2026-05-16.md (171 lines): multipole-vector extraction algorithm (de Oliveira-Costa 2004), visibility-function modelling for T-vs-E recombination Δz, predicted differential trajectory across Δz ∈ [10, 50] (range 0.67° → 3.4°; central 1.0°–2.0°), falsifier threshold Δθ_TE < 0.1°. Lives in research-mfo/ alongside the dark-sector + AoE working notes.
No code change; no ABI change; no Python API change¶
- C ABI v2 unchanged.
- No new C symbols, no new Python catalog modules, no new tool_schema entries.
- Data-only addition + research-note artifact + version bump (4 SSOT files + CHANGELOG).
Versioning¶
0.4.1rc2 → 0.4.1rc3. Cumulative rc-stack on the 0.4.1 cosmos catalog sprint per [[feedback_rc_stacking_versioning]]. Sprint accumulates: rc1 (cosmos catalog data layer + framework precedent for srmech-primary catalogs) + rc2 (read_ndjson skips # comments framework fix) + rc3 (this — fourth catalog source). Clean 0.4.1 ships to production once the sprint accumulates everything the cosmos-catalog research thread needs.
[0.4.1rc2] - 2026-05-16¶
Framework bug fix — read_ndjson now skips # comment-header lines. Found during the rc1 TestPyPI smoke verify when catalog.attestation_audit failed on the new cmb_polarisation_spectra/row.ndjson. Investigation confirmed the bug pre-exists in production srmech 0.4.0 and affects every #-comment-prefixed NDJSON across the spectral-research portfolio — including ephemerides-spectral 0.29.x's already-shipped cmb_anomalies + cmb_power_spectrum catalogs. catalog.get_attested_dataset was comment-aware via a different code path; catalog.attestation_audit calls read_ndjson directly and was choking on the leading # CMB ... catalogue header.
Fixed — format.read_ndjson skips # lines + empty lines uniformly¶
- Pure-Python path (
format.py:286–296) now skips lines that matchnot line or line.startswith("#")after stripping whitespace. - Native path (
format.py:266–283) now decodes each line, lstrips whitespace, and skips empty +#-prefixed lines before callingMPRRecord.from_json_line. Indented comments (leading whitespace before#) are also tolerated. - New ratchet test
test_format.test_ndjson_skips_hash_comment_linespins the behaviour across both paths.
This restores attestation_audit parity with get_attested_dataset for all #-comment-prefixed NDJSON catalogs.
Versioning¶
0.4.1rc1 → 0.4.1rc2. Patch bump on the cosmos catalog sprint. Cumulative rc-stack per [[feedback_rc_stacking_versioning]]; clean 0.4.1 ships to production after rc2 TestPyPI verify covers both the cosmos catalog content (rc1) and the framework fix (rc2).
[0.4.1rc1] - 2026-05-16¶
Cosmos catalog ship rc1. Seeds three new srmech-primary attested AMSC sources covering the Planck 2018 PR3 CMB observables that downstream MFO research needs as ground-proof anchors. Per user directive on the AoE/dark-sector research line (PR #437 + the four-turn dialog landed in MFO §VII.6.2/.6.3): cosmos catalog lives in srmech for now (MFO + srmech ship as one demonstrates every primitive class operator); future migration to ephemerides-spectral is later scope.
Added — three attested catalogs under srmech/amsc/attested/¶
| Source | Rows | Primary reference | Canonical content |
|---|---|---|---|
cmb_polarisation_spectra |
45 | Planck 2018 V (Aghanim et al., A&A 641 A5; arXiv:1907.12875) | Binned TE/EE bandpowers (PR3/R3.02) + low-ℓ BB upper limits (R3.01); TE acoustic peak ℓ=315 D_ℓ=119.4±2.5 μK²; EE 3rd peak ℓ≈1005 D_ℓ=42.4±1.3 μK²; BB Planck-range noise-dominated |
cmb_bispectrum |
36 | Planck 2018 IX (Akrami et al., A&A 641 A9; arXiv:1905.05697) | f_NL constraints (local / equilateral / orthogonal × KSW / binned / modal methods); SMICA T+E KSW lensing-subtracted: f_NL^local = -0.9 ± 5.1, f_NL^equilateral = -18 ± 47, f_NL^orthogonal = -37 ± 23 — all consistent with Gaussianity |
cmb_lensing |
37 | Planck 2018 VIII (Aghanim et al., A&A 641 A8; arXiv:1807.06210) | Lensing reconstruction Cℓ^{ϕϕ} bandpowers + MV amplitude; Â^{φ,MV}_{8→400} = 1.011 ± 0.028 (conservative); 40σ MV detection |
All 118 rows authored via PDF extraction per [[feedback_pdf_extraction_citation_discipline]] — three primary arXiv IDs verified clean at first-page extraction (no citation drift). PLA non-blocking; arXiv served all three PDFs.
Architectural note — srmech-primary catalogs¶
This is the first time srmech itself hosts attested catalogs (hitherto srmech was the AMSC framework provider, with catalogs hosted in consumer packages like ephemerides-spectral). The new sources sit at srmech/amsc/attested/<source>/ where _attested_root() finds them automatically — no register_attested_root() call needed. Existing ephemerides-spectral catalogs continue to register their own root via the cross-package bootstrap (Phase 2 of Task #197).
No code change; no ABI change¶
- C ABI v2 unchanged. No new C symbols. No JPL audit pin changes.
- No
srmech.amsc.<class>Python surface changes. - No
srmech.qm.*operation changes. - Data-only ship: descriptors + NDJSON + schemas under
srmech/amsc/attested/. - C version macros bump
SRMECH_VERSION_PATCH0 → 1 andSRMECH_VERSION_PRE"" → "rc1".
Versioning¶
0.4.0 → 0.4.1rc1. Patch bump (data addition; no API or ABI change). rc1 routes to TestPyPI per the existing publish-workflow regex; the clean v0.4.1 ships to production PyPI after rc verify.
[0.4.0] - 2026-05-15¶
Phase C1 close — production ship. Ships the cumulative Phase C1 scope (rc1 → rc12) to production PyPI. Per [[feedback_rc_stacking_versioning]], the rc-stack accumulated during the sprint; this clean-semver tag promotes the verified rc12 state to live.
Phase C1 cumulative scope (per [[feedback_no_mvp_framing]] full-coverage shipping)¶
Primitive vocabulary — 14 of 14 classes with C surfaces. Closes Task #217 Phase C1 / the per-class C parity build-out per [[feedback_no_binding_layer_carveout]]:
- Class A — content-addressing via SHA-256 (rc-baseline)
- Class B — tagged-tuple TLV byte-canonical form (rc4)
- Class C — streaming iteration via NDJSON tokenisation (rc-baseline)
- Class D — late-binding multi-needle pattern dispatch (rc5)
- Class E — catalog sorted-key binary-search lookup (rc5)
- Class F — substitution /
{key}template render (rc5) - Class G — byte-pattern search (rc4)
- Class H — self-introspection (rc4 acknowledgment of existing version / ABI accessors)
- Class I — cyclic-group / modular arithmetic (rc1)
- Class J — prime-factorisation / period (rc3)
- Class K — equation-of-centre / pin-slot (rc7) — Kepler (1609); Smith (1979); Brouwer-Clemence (1961); Freeth (2021) Supp S9
- Class L — graph Laplacian; pi-free Jacobi eigvals (rc2)
- Class M — HDC binary spatter codes — bind/bundle/permute/similarity (rc8) — Kanerva (2009); Plate (1995); Rachkovskij (2001)
- Class N — rational-approximation; continued-fraction convergents (rc6)
Canonical QM/QFT/SM operations layer (srmech.qm.*). Sourced from canonical physics literature per [[feedback_science_is_ssot_not_project]]:
single_particle(rc9) — TDSE / TISE / Heisenberg evolution / [x̂,p̂] / lattice momentum / density matrix / Liouville-vN. Schrödinger (1926); Heisenberg (1925); Sakurai §§1.4, 1.6, 2.1-2.3, 3.4; von Neumann (1932); Wilson (1974)spin(rc9) — Pauli matrices σ_x/σ_y/σ_z, Clifford Cl(0,3) residual verification, arbitrary-axis spin-½ operator. Pauli (1927); Sakurai §3.2potentials(rc9) — hydrogen radial Schrödinger, harmonic oscillator ladder operators. Bohr (1913); Heisenberg (1925); Born-Heisenberg-Jordan (1926); Sakurai §§2.3, 3.7relativistic(rc10) — Dirac γ-matrices (Cl(1,3)), γ_5, Weyl projectors, charge conjugation (Majorana), Dirac operator, Klein-Gordon dispersion. Dirac (1928); Klein/Gordon (1926); Weyl (1929); Majorana (1937); Peskin-Schroeder §§3.2-3.4propagators(rc10) — Feynman scalar / fermion / photon / massive-vector. Feynman (1949); Dyson (1949); Peskin-Schroeder §§4.2, 4.7-4.8, 20.1; Weinberg Vol II §21.1pseudo_hermitian(rc10) — η-deformed inner product, expectation, η-pseudo-Hermiticity test, η construction, real-spectrum theorem. Closes chess-spectral ADR-005 framework gap. Bender & Boettcher (1998); Mostafazadeh (2002, 2010)gauge(rc11) — SU(2) / SU(3) Gell-Mann generators, structure constants, Lie algebra residuals, Casimirs (¾ for SU(2) fund, 4/3 for SU(3) fund), gauge connection, Wilson loop. Yang-Mills (1954); Gell-Mann (1962); Wilson (1974); Peskin-Schroeder §§15-17sm(rc11) — Higgs potential / vev, weak mixing angle, W/Z boson masses, Weinberg relation, fermion mass from Yukawa, CKM matrix (Chau-Keung parameterization). Glashow (1961); Weinberg (1967); Salam (1968); Higgs (1964); Cabibbo (1963); Kobayashi-Maskawa (1973); Peskin-Schroeder Chs 20-21
Ontology refinement (notebook ship, rc7-rc10 cumulative).
- MFO §VII.1.2 — 1D_t as the Laws of Everything — compressed-cascade content — per user direction 2026-05-15, with user's canonical compressions preserved verbatim (
memory/user_stance_1d_t_as_storage_extraction.mdoperation-level +memory/user_stance_1d_collapse_to_loe_identity_not_action.mdidentity-level refinement). - Identity-not-implementation discipline named as umbrella pattern unifying the shadow-stance family (
memory/user_stance_identity_not_implementation_discipline.md). - Plurality of "Laws of Everything" canonical (
memory/reference_loe_plural_canonical.md). - Two concertmaster artifacts in
docs/srmech/notes/:1d_t_as_storage_extraction_2026-05-15.md+1d_collapse_to_loe_identity_2026-05-15.md. - Plus
task_218_phase_c2_chess_spectral_qm_audit_2026-05-15.md— QM stack coverage audit (informed Phase C2 → folded into Phase C1 per[[feedback_science_is_ssot_not_project]]).
Tool-schema audit (rc12) closes the sprint — srmech.amsc.tool_schema extended with ~87 entries covering all 14-class primitives + the full srmech.qm.* operations layer. Coverage ratchet test (tests/test_tool_schema_coverage.py, 8 cases) walks every public callable in srmech.amsc.* and srmech.qm.* via pkgutil + inspect and asserts each has a registered ToolEntry. Closes Tasks #219 + #220.
Test plan summary¶
- CI: 8/8 pass at every rc (rc7-rc12), all three OS cells (Ubuntu / macOS / Windows) × Python 3.10-3.14 × pedantic C build (gcc / clang / MSVC) + pure-wheel build + sdist.
- Cumulative test count growth: ~290 cases across the 12-rc sprint covering Class K parity, Class M parity, QM single-particle / spin / potentials / relativistic / propagators / pseudo-Hermitian / gauge / sm operations layer canonical identities, and tool-schema coverage.
- TestPyPI verification:
srmech-v0.4.0rc12published + smoke-verified prior to this clean-semver ship.
Discipline honoured¶
[[feedback_no_mvp_framing]], [[feedback_science_is_ssot_not_project]], [[feedback_no_privileged_primitive_classes]], [[feedback_no_binding_layer_carveout]], [[feedback_jpl_rule_5_two_assert_habit]], [[feedback_rc_stacking_versioning]], [[feedback_no_squash_merges]], [[feedback_pdf_extraction_citation_discipline]], [[user_stance_kepler_shape_universal]], [[user_stance_1d_collapse_to_loe_identity_not_action]], [[user_stance_identity_not_implementation_discipline]].
Changed¶
- ABI stays v2 — cumulative across rc7-rc12 (pure additions per Phase B4 convention).
[0.4.0rc12] - 2026-05-15¶
Added¶
Task #217 Phase C1 — end-of-sprint tool-schema audit (Tasks #219 + #220).
Twelfth and final canonical rc in Phase C1's rc-stacked build-out. Closes the end-of-sprint hygiene scope per user direction ("check tool-schema and help arg that every command is shown how to be used") before 0.4.0 ships to PyPI.
srmech.amsc.tool_schema — extension to cover the full operations layer¶
Adds two new registration functions to srmech.amsc.tool_schema:
_register_primitive_class_tools()— 27 entries covering the 14-class Spike #24 primitive vocabulary (Classes A and C were already registered; this adds B, D, E, F, G, I, J, K, L, M, N — every primitive operation exposed viasrmech.amsc.*)._register_qm_tools()— 54 entries covering the canonical QM/QFT/SM operations layer insrmech.qm.*(single_particle, spin, potentials, relativistic, propagators, pseudo_hermitian, gauge, sm).
Both functions are called at tool_schema module import time alongside the original _register_amsc_tools(). Total registered: ~87 tool-schema entries, each with name + owner + category + summary + parameters + returns. Summaries cite canonical SSoT per [[feedback_science_is_ssot_not_project]].
Coverage ratchet — tests/test_tool_schema_coverage.py¶
New test file with 8 cases enforcing the audit at CI time:
test_amsc_public_callables_have_tool_entries— walkssrmech.amsc.*viapkgutil+inspect; every public function (minus a small exempt allowlist of bridge helpers + adapters + profile-loader internals) must have a registered entry.test_qm_public_callables_have_tool_entries— same forsrmech.qm.*. No exemptions — every public callable must be registered.test_tool_schema_entries_have_required_fields— non-empty name / owner / summary; parameters tuple well-typed.test_tool_schema_owner_is_srmech_for_builtins— owner = "srmech" for builtin entries.test_tool_schema_view_is_jsonable— JSON round-trip clean.test_tool_schema_total_count_meets_floor— ratchet at ≥ 80 entries (only ever grows).test_no_duplicate_tool_names— each dotted name unique.test_tool_schema_categories_match_module_structure— category sanity-check.
Discipline notes¶
- No CLI surface to audit — srmech is a library, not a command-line tool. The user's "every command is shown how to be used" direction was interpreted as: every callable has a proper docstring (already enforced through rc7-rc11 ToolEntry / docstring discipline) and every callable surfaces via the tool-schema introspection API (this rc).
- Per-operation canonical SSoT preserved: every new ToolEntry's summary cites the canonical physics literature (Schrödinger / Heisenberg / Dirac / Yang-Mills / Gell-Mann / Wilson / Glashow-Weinberg-Salam / Cabibbo / Kobayashi-Maskawa / Higgs / Mostafazadeh / Bender-Boettcher) per
[[feedback_science_is_ssot_not_project]]. - No new C symbols; ABI stays v2.
Changed¶
srmech.amsc.tool_schema: +2 registration functions, +81 new ToolEntry registrations (cumulative).
Roadmap¶
Phase C1 close → 0.4.0 final:
- 14 of 14 primitive classes with C surfaces ✅
- Canonical single-particle QM (rc9) ✅
- Relativistic QM + Feynman propagators + η-pseudo-Hermitian (rc10) ✅
- Gauge theory + Standard Model surface (rc11) ✅
- End-of-sprint tool-schema audit (rc12, this rc) ✅
Next: drop the rc12 suffix → tag srmech-v0.4.0 → autotag dispatches production PyPI publish per the existing publish workflow (Task #196).
[0.4.0rc11] - 2026-05-15¶
Added¶
Task #217 Phase C1 — Gauge theory + Standard Model surface (final canonical-physics rc).
Eleventh rc in Phase C1's rc-stacked build-out. Closes the canonical-physics scope of PR #432: 14/14 primitive classes + canonical single-particle QM (rc9) + relativistic QM / propagators / η-pseudo-Hermitian (rc10) + gauge theory + SM surface (rc11).
Per [[feedback_science_is_ssot_not_project]]: each operation cites canonical gauge-theory / SM literature. Numerical experimental values (M_W, M_Z, fermion masses, CKM elements) are NOT hardcoded — this rc ships the algebraic primitives that map (gauge couplings, Higgs vev, Yukawa couplings, mixing angles) to observable masses.
Per [[user_stance_1d_collapse_to_loe_identity_not_action]]: substrate-coupling operations on internal-symmetry representation spaces. Each dissolves into the 14-class primitive vocabulary per [[feedback_no_privileged_primitive_classes]] — no new classes.
srmech.qm.gauge¶
| Operation | Canonical SSoT | 14-class dissolution |
|---|---|---|
su2_generators() → (T¹, T², T³) |
Peskin-Schroeder §15.1 eq 15.5-6 | Class M (Lie-algebra binding, Pauli-half generators) |
su2_structure_constants() → εᵃᵇᶜ |
Peskin-Schroeder §15.1 eq 15.4 | — |
su3_gell_mann_matrices() → (λ¹...λ⁸) |
Gell-Mann (1962) PR 125, 1067; Peskin-Schroeder eq 17.32 | Class M (SU(3) Lie-algebra binding) |
su3_generators() → (T¹...T⁸) |
Peskin-Schroeder eq 17.33 | Class M |
su3_structure_constants() → fᵃᵇᶜ |
Peskin-Schroeder eq 17.34; Schwartz Table 25.1 | — |
lie_algebra_residual(gens, f) |
Peskin-Schroeder §15.1 eq 15.4 | (verification: [Tᵃ, Tᵇ] = i fᵃᵇᶜ Tᶜ) |
casimir_operator(gens) → C₂ |
Peskin-Schroeder §15.4 eq 15.93 | Class L (sum of generator squares) |
casimir_eigenvalue(gens) |
Peskin-Schroeder §15.4 | Class L (trace / dim) |
gauge_connection_matrix(A, gens) → Aᵃ Tᵃ |
Peskin-Schroeder §15.1 eq 15.2 | Class M |
gauge_path_segment(A, gens, g) → exp(i g Aᵃ Tᵃ) |
Wilson (1974) PRD 10, 2445; Peskin-Schroeder §15.3 eq 15.55 | Class L (Hermitian matrix exponential via eigendecomp) |
wilson_loop_from_segments(A_segs, gens, g) → ∏ exp(...) |
Wilson (1974) eq 2.3 | Class C ∘ Class L (path-ordered iteration over segments) |
srmech.qm.sm¶
| Operation | Canonical SSoT | 14-class dissolution |
|---|---|---|
higgs_potential(φ, μ², λ) |
Higgs (1964); Peskin-Schroeder §20.1 eq 20.6 | Class K (continuous projection) |
higgs_vev(μ², λ) → v |
Peskin-Schroeder §20.1 eq 20.7 | Class K |
weak_mixing_angle(g, g') |
Weinberg (1967) eq 8; Peskin-Schroeder §20.2 eq 20.31 | Class K (atan2) |
w_boson_mass(g, v) |
Peskin-Schroeder §20.2 eq 20.30 | Class K |
z_boson_mass(g, g', v) |
Peskin-Schroeder §20.2 eq 20.32 | Class K |
weinberg_relation_residual(g, g', v) |
Peskin-Schroeder §20.2 eq 20.33 | (verification: M_W = M_Z cos θ_W) |
electroweak_summary(g, g', v) → dict |
Composite §20.2 | — |
fermion_mass_from_yukawa(y, v) |
Peskin-Schroeder §20.2 eq 20.27; Schwartz §29.1 eq 29.18 | Class K |
ckm_matrix(θ₁₂, θ₁₃, θ₂₃, δ_CP) |
Cabibbo (1963); Kobayashi-Maskawa (1973); Chau-Keung (1984); PDG §12.1 | Class M (unitary mixing-binding) |
ckm_unitarity_residual(V) |
PDG §12.1 | (verification) |
Foundation literature: - Glashow (1961) Nucl. Phys. 22, 579-588. - Weinberg (1967) Phys. Rev. Lett. 19, 1264-1266. - Salam (1968) Elementary Particle Theory. - Higgs (1964); Englert-Brout (1964); Guralnik-Hagen-Kibble (1964). - Cabibbo (1963); Kobayashi-Maskawa (1973). - Yang & Mills (1954) Phys. Rev. 96, 191-195. - Gell-Mann (1962) Phys. Rev. 125, 1067-1084. - Wilson (1974) Phys. Rev. D 10, 2445-2459. - Peskin & Schroeder (1995) Intro QFT, Chs 15-17, 20-21. - Weinberg (1996) QToF Vol II §15, §21. - Schwartz (2014) QFT and the SM, Chs 25-29.
Tests (2 files, ~40 cases)¶
test_qm_gauge.py— SU(2)/SU(3) generators Hermitian + traceless; canonical normalizationtr(TᵃTᵇ) = δᵃᵇ/2; structure-constant total antisymmetry; Lie algebra closure[Tᵃ, Tᵇ] = i fᵃᵇᶜ Tᶜat machine precision for both SU(2) and SU(3); Casimir eigenvalue ¾ for SU(2) fundamental, 4/3 for SU(3) fundamental; Casimir proportional to identity (Schur); path-segment unitarity; multi-segment Wilson-loop unitarity.test_qm_sm.py— Higgs vev formula; potential minimum at vev;V(v) = -μ⁴/(4λ); Weinberg relationM_W = M_Z cos θ_Wat machine precision across multiple coupling regimes; fermion massm = y v / √2; CKM unitarityV V† = Ifor arbitrary mixing angles + CP phase; CKM reduces to 2×2 Cabibbo rotation when θ₁₃ = θ₂₃ = 0.
Changed¶
- ABI stays v2 — operations layer is pure Python (numpy-based; gauge matrix exponentials via Hermitian eigendecomp, no scipy dependency).
- srmech.qm imports updated for the two new submodules (
gauge,sm).
Roadmap¶
Phase C1 canonical-physics scope complete: - 14 of 14 primitive classes with C surfaces. - Canonical single-particle QM (rc9). - Relativistic QM + Feynman propagators + η-pseudo-Hermitian (rc10). - Gauge theory + Standard Model surface (rc11, this rc).
Remaining for Phase C1 close → 0.4.0 final (folding all into PR #432):
- End-of-sprint hygiene per user direction:
- Task #219: Per-class CLI --help audit — every command shows how to be used.
- Task #220: Tool-schema extension — every operation surfaces via srmech.amsc.tool_schema.
- 0.4.0 final: clean ship to PyPI at PR merge.
[0.4.0rc10] - 2026-05-15¶
Added¶
Task #217 Phase C1 — Relativistic QM + Feynman propagators + η-pseudo-Hermitian primitive.
Tenth rc in Phase C1's rc-stacked build-out. Folds the relativistic-QM / QFT propagator layer into PR #432 with canonical literature SSoT per [[feedback_science_is_ssot_not_project]].
Per [[user_stance_1d_collapse_to_loe_identity_not_action]]: these are substrate-coupling operations on relativistic-QM and QFT Hilbert spaces. Each dissolves into the 14-class primitive vocabulary per [[feedback_no_privileged_primitive_classes]]. No new primitive classes.
Metric convention: mostly-minus η^{μν} = diag(+1, -1, -1, -1) (Peskin-Schroeder convention). γ-matrix representation: Dirac (standard) basis.
srmech.qm.relativistic¶
| Operation | Canonical SSoT | 14-class dissolution |
|---|---|---|
minkowski_metric() |
Peskin-Schroeder §3.1 eq 3.4 | — |
gamma_matrices() → (γ^0, γ^1, γ^2, γ^3) |
Dirac (1928); Peskin-Schroeder §3.2 eq 3.25 + A.6 | Class M (Cl(1,3) Clifford binding) |
gamma_5() |
Peskin-Schroeder §3.4 eq 3.72 | Class M |
clifford_residuals() |
Peskin-Schroeder §3.2 eq 3.21, §3.4 eq 3.72 | (verification) |
weyl_left_projector(), weyl_right_projector() |
Weyl (1929); Peskin-Schroeder §3.4 eq 3.71 | Class M |
charge_conjugation_matrix() |
Majorana (1937); Peskin-Schroeder eq A.27 | Class M |
dirac_operator_momentum_space(k, m) |
Dirac (1928); Peskin-Schroeder §3.2 eq 3.45-3.46 | Class L (linear operator on spinor space) |
klein_gordon_dispersion(k, m) |
Klein (1926); Gordon (1926); Peskin-Schroeder §2.3 eq 2.39 | Class L |
four_momentum_squared(k) |
Peskin-Schroeder §3.1 eq 3.4 | — |
srmech.qm.propagators¶
| Operation | Canonical SSoT | 14-class dissolution |
|---|---|---|
feynman_scalar_propagator(k², m, ε) |
Feynman (1949); Dyson (1949); Peskin-Schroeder §4.2 eq 4.42 | Class K (continuous projection-shadow of integer-cyclic upstream per the lattice scalar propagator G(k) = 1/(m² + k̂²)) |
feynman_fermion_propagator(k, m, ε) |
Peskin-Schroeder §4.7 eq 4.107 + 4.111 | Class K + Class M |
feynman_photon_propagator(k², ξ, ε, k) |
Peskin-Schroeder §4.8 eq 4.118-4.121 | Class K (covariant gauge + Feynman gauge specializations) |
feynman_massive_vector_propagator(k, m, ε) |
Peskin-Schroeder §20.1 eq 20.13; Weinberg Vol II §21.1.21 | Class K |
srmech.qm.pseudo_hermitian¶
Closes the η-metric primitive gap in chess-spectral ADR-005 per docs/srmech/notes/task_218_phase_c2_chess_spectral_qm_audit_2026-05-15.md. Chess-spectral becomes a substrate-consumer of these primitives when ADR-005 is finished.
| Operation | Canonical SSoT | 14-class dissolution |
|---|---|---|
inner_product_eta(a, b, η) |
Mostafazadeh (2002) JMP 43, 205, eq 2.6 | Class L (η-deformed inner product) |
expectation_eta(O, ψ, η) |
Mostafazadeh (2002) eq 3.6 | Class L |
is_pseudo_hermitian(O, η) |
Mostafazadeh (2002) eq 2.4 | (verification) |
construct_eta_from_eigendecomposition(O) |
Mostafazadeh (2002) eq 2.7-2.10 | Class L (eigendecomp + inverse) |
pseudo_hermitian_eigenvalues_real(O, η) |
Bender & Boettcher (1998) PRL 80, 5243; Mostafazadeh (2002, 2010) | (verification) |
Foundation literature: - Bender, C.M. & Boettcher, S. (1998) Phys. Rev. Lett. 80, 5243-5246. - Mostafazadeh, A. (2002) J. Math. Phys. 43, 205-214; 2814-2816; 3944. - Mostafazadeh, A. (2010) Int. J. Geom. Methods Mod. Phys. 7, 1191-1306.
Tests (3 files, ~40 cases)¶
test_qm_relativistic.py— Cl(1,3) algebra at machine precision; Weyl projector identities (P_L + P_R = I, P² = P, P_L P_R = 0); charge conjugationC γ^μ C^{-1} = -(γ^μ)^T; Klein-Gordon dispersionE² = |k|² + m²; Dirac operator on-shell zero-eigenvalues at rest; positive-energy spinor annihilation.test_qm_propagators.py— scalar / fermion propagator inverses;S_F^{-1}(k) = -i(γ·k - m)/(k²-m²)verified via(γ·k - m) S_F = i I_4; on-shell pole-prescription handling; photon Feynman-gauge shape; massive-vector k^μ k^ν / m² term verification.test_qm_pseudo_hermitian.py— η = I reduction to standard inner product; constructed η makes operator η-pseudo-Hermitian; Mostafazadeh real-spectrum theorem; complex-spectrum rejection.
Changed¶
- ABI stays v2 — operations layer is pure Python (numpy-based for complex matrices).
- srmech.qm: imports updated for the three new submodules (
relativistic,propagators,pseudo_hermitian).
Roadmap¶
Phase C1 progress: 14 of 14 primitive classes + canonical single-particle QM + relativistic QM + Feynman propagators + η-pseudo-Hermitian shipped.
Remaining for Phase C1 close → 0.4.0 final (folding all into PR #432):
- rc11: Gauge theory (U(1) / SU(2) / SU(3) Yang-Mills, Wilson loops, gauge connections, Casimir per irrep) + SM surface (electroweak unification, Higgs, Yukawa).
- End-of-sprint: Task #219 (per-class CLI --help audit) + Task #220 (tool-schema extension for every operation).
- 0.4.0 final: clean ship to PyPI at PR merge.
[0.4.0rc9] - 2026-05-15¶
Added¶
Task #217 Phase C1 — Canonical single-particle QM operations layer (first ship from srmech.qm).
Ninth rc in Phase C1's rc-stacked build-out. First substantive operations layer on top of the 14-class C parity roster — opens srmech.qm.* as the canonical QM/QFT/SM operations namespace, with each operation sourced from canonical physics literature per [[feedback_science_is_ssot_not_project]] (Sakurai / Cohen-Tannoudji / Griffiths / Pauli / Schrödinger / Heisenberg / Bohr / von Neumann).
Per [[user_stance_1d_collapse_to_loe_identity_not_action]] (MFO §VII.1.2): these operations are substrate-coupling operations that uncompress LoE-content (1D_t Laws) into event-stream. Each dissolves into the 14-class primitive vocabulary per [[feedback_no_privileged_primitive_classes]] — no new classes added.
srmech.qm.single_particle¶
| Operation | Canonical SSoT | 14-class dissolution |
|---|---|---|
tdse_evolve(H, psi, t) |
Schrödinger (1926); Sakurai §2.1.5 | Class L (spectral evolution V·diag(exp(-iλt))·V^H) |
tise_solve(H) |
Schrödinger (1926); Sakurai §2.1.3 | Class L (Hermitian eigendecomp) |
commutator(A, B) = AB - BA |
Sakurai §1.4 eq 1.4.6 | Class L (operator algebra) |
heisenberg_evolve(A, H, t) |
Heisenberg (1925); Sakurai §2.2 eq 2.2.15 | Class L (eigenbasis-diagonal U†AU) |
lattice_momentum(n, dx) |
Sakurai §1.6; Wilson (1974) | Class C (lattice gradient as anti-Hermitian central difference) |
density_matrix(psi) |
von Neumann (1932); Sakurai §3.4 eq 3.4.7 | (pure-state outer product) |
liouville_evolve(rho, H, t) |
von Neumann (1932); Sakurai §3.4.2 eq 3.4.28 | Class L (commutator-flow) |
srmech.qm.spin¶
| Operation | Canonical SSoT | 14-class dissolution |
|---|---|---|
pauli_matrices() → (σ_x, σ_y, σ_z) |
Pauli (1927) ZfP 43, 601; Sakurai §3.2 | Class M (Clifford Cl(0,3) binding generators) |
pauli_clifford_residuals() |
Sakurai §3.2 eq 3.2.2-3 | (verification: {σ_i, σ_j} = 2δ_{ij} I, [σ_i, σ_j] = 2i ε_{ijk} σ_k) |
pauli_spin_operator(direction) |
Sakurai §3.2 eq 3.2.51 | Class M (Clifford projection along arbitrary axis) |
srmech.qm.potentials¶
| Operation | Canonical SSoT | 14-class dissolution |
|---|---|---|
hydrogen_radial(n_grid, r_max, l_quantum) |
Bohr (1913); Schrödinger (1926); Sakurai §3.7 | Class L (3-point-stencil radial-Laplacian eigendecomp) |
harmonic_oscillator_ladder(n_dim, omega) |
Heisenberg (1925); Born-Heisenberg-Jordan (1926); Sakurai §2.3 | Class M (Fock-space binding for a, a†) |
harmonic_oscillator_hamiltonian(n_dim, omega) |
Sakurai §2.3 eq 2.3.16 | Class L + Class M composition (H = ω(a†a + 1/2)) |
Tests (3 files, ~50 cases)¶
tests/test_qm_single_particle.py— TDSE norm/energy preservation, eigenstate phase evolution; TISE orthonormality + eigen-relation; commutator self-zero + antisymmetry; Heisenberg self-conservation; lattice momentum Hermiticity; density matrix idempotency for pure states; Liouville trace + purity preservation.tests/test_qm_spin.py— Pauli Hermiticity / tracelessness / eigenvalues ±1 / Clifford algebra residuals at machine precision; arbitrary-axis spin-½ operator.tests/test_qm_potentials.py— Harmonic oscillator analytical spectrum (E_n = ω(n + 1/2)); ladder actiona|n⟩ = √n |n-1⟩; hydrogen ground state ≈ −0.5 Rydberg; 2s state ≈ −0.125; l=1 centrifugal exclusion; TDSE-on-oscillator-eigenstate phase consistency.
Changed¶
- ABI stays v2 — no new C symbols (operations layer is Python-side, building on Classes A-N C primitives + numpy for complex-Hermitian operations).
Roadmap¶
Phase C1 progress: 14 of 14 primitive classes + single-particle QM operations layer landed.
Remaining for the Phase C1 close → 0.4.0 final (folding all into PR #432):
- rc10: Relativistic QM (Klein-Gordon, Dirac, Weyl, Majorana, Bargmann-Wigner) + Feynman propagators (scalar / fermion / photon / vector) + η-pseudo-Hermitian (closes ADR-005 in chess-spectral).
- rc11: Gauge theory (U(1) / SU(2) / SU(3) Yang-Mills, Wilson loops, gauge connections, Casimir per irrep) + Standard Model surface (electroweak unification, Higgs, Yukawa couplings).
- End-of-sprint hygiene: Task #219 (per-class CLI --help audit — every command shows how to be used) + Task #220 (tool-schema extension — every operation surfaces via srmech.amsc.tool_schema).
- 0.4.0 final: clean ship to PyPI at PR merge.
[0.4.0rc8] - 2026-05-15¶
Added¶
Task #217 Phase C1 — Class M (HDC binary spatter codes) C port. Closes the 14-class C parity roster.
Eighth rc in Phase C1's rc-stacked build-out. Class M is the binding operation that uncompresses LoE-content along its compression axis per [[user_stance_1d_collapse_to_loe_identity_not_action]] — substrate-coupling operation, NOT the LoE-content itself (1D_t is the content per MFO §VII.1.2). Class C ∘ Class M composes the full LoE-uncompression kernel: Class C iteration drives Class M binding to produce event-stream from compressed-cascade laws-content.
Four BSC operations on byte-buffer hyperdimensional vectors (D bits = 8 * n_bytes; canonical default 128 bytes = 1024 bits):
| C symbol | Python wrapper | Operation | Canonical SSoT |
|---|---|---|---|
srmech_hdc_bind(a, b, n_bytes, *out) |
srmech.amsc.hdc.bind(a, b) |
Component-wise XOR; commutative, associative, self-inverse. | Kanerva (2009) Cognitive Computation 1, 139-159 |
srmech_hdc_bundle(vectors, n_vectors, n_bytes, *out) |
srmech.amsc.hdc.bundle(vectors) |
Bitwise majority across odd n_vectors ≤ 257. Even counts rejected (caller can pad with tie-breaker). |
Plate (1995) IEEE TNN 6, 623-641 |
srmech_hdc_permute(a, n_bytes, rotate_bits, *out) |
srmech.amsc.hdc.permute(a, rotate_bits) |
Cyclic bit-rotation; preserves popcount; permute(permute(a, k), -k) == a. |
Rachkovskij (2001) Neural Comput Appl 9, 322 |
srmech_hdc_similarity(a, b, n_bytes, *out) |
srmech.amsc.hdc.similarity(a, b) |
1 - 2 * hamming(a, b) / D in [-1, 1]; +1 identical, 0 orthogonal, -1 complementary. |
Kanerva (2009) |
SSoT discipline per [[feedback_science_is_ssot_not_project]]. Each operation cites canonical HDC literature — Kanerva / Plate / Rachkovskij — not any project instantiation. Chess-spectral's encoder (the 640-dim bundle that gets cast to ψ via state_to_psi) becomes one substrate-consumer of these primitives.
JPL Power-of-Ten compliant: bounded loops (bundle ≤ MAX_BUNDLE_N=257 vectors; permute ≤ D bits), 256-entry popcount lookup table for portability (no __builtin_popcount dependency).
Changed¶
- ABI stays v2 — four new symbols are pure additions per the Phase B4 convention.
- CMake:
srmech_hdc.cpicked up automatically byfile(GLOB CONFIGURE_DEPENDS c/src/*.c).
Roadmap¶
Phase C1 progress: 14 of 14 classes shipped with C surfaces ✅ (A + C from Phase B; I + L + J + B + G + H + D + E + F + N + K + M from rc1–rc8). 14-class C parity roster CLOSED.
Next layers in PR #432 (Phase C1 close — folding all): - Canonical single-particle QM (TDSE / TISE / Heisenberg / [x̂,p̂] / Liouville-vN / Pauli / hydrogen-radial / harmonic-oscillator) — Sakurai / Cohen-Tannoudji / Griffiths. - Relativistic QM + Feynman propagators + η-pseudo-Hermitian — Peskin & Schroeder Chs 3-4; Bender & Boettcher (1998); Mostafazadeh (2002, 2010). - Gauge theory + SM — Peskin & Schroeder Chs 15, 20-21; Weinberg Vol II. - 0.4.0 final — clean ship to PyPI at PR merge.
[0.4.0rc7] - 2026-05-15¶
Added¶
Task #217 Phase C1 — Class K (equation-of-centre / pin-slot) C port.
Seventh rc in Phase C1's rc-stacked build-out. Class K is the continuous-projection layer of Kepler-shape primitive composition — per [[user_stance_kepler_shape_universal]] + PR #416 F2/F15/F17, Kepler-equation algebra IS pin-slot composition. The bronze Antikythera instantiates Class K natively; the universe instantiates the same algebra via gravitational dynamics (see [[user_stance_1d_t_as_storage_extraction]] + docs/srmech/notes/1d_t_as_storage_extraction_2026-05-15.md — same Kepler-shape cascade at different dimensional reaches).
Three continuous operations on double-precision floats (uses libm: sin / cos / atan2 / fabs):
| C symbol | Python wrapper | Operation | Canonical SSoT |
|---|---|---|---|
srmech_pin_slot(theta, i, d, *phi) |
srmech.amsc.kepler.pin_slot(theta, i, d) |
Era-appropriate Antikythera pin-and-slot transform: phi = atan2(i*sin(theta), d + i*cos(theta)). |
Freeth (2021) Nature Sci Rep, Supp S9 |
srmech_kepler_solve(M, e, tol, max_iter, *E) |
srmech.amsc.kepler.kepler_solve(M, e) |
Newton-Raphson on Kepler's equation M = E - e*sin(E) with Smith (1979) initial-guess starter E_0 = M + e*sin(M). Converges in 4-6 iter for e < 0.5. |
Kepler (1609) Astronomia Nova; Smith (1979) Celestial Mech 19, 163 |
srmech_equation_of_centre(M, e, n_terms, *delta) |
srmech.amsc.kepler.equation_of_centre(M, e, n_terms) |
Fourier-series principal-term-per-harmonic nu - M = sum_{k=1..n} c_k * e^k * sin(k*M) with c_k = [2, 5/4, 13/12, 103/96, 1097/960, 1223/960] for k = 1..6. |
Brouwer & Clemence (1961) §3.2; Murray & Dermott (1999) §2.5 eq 2.84-2.88 |
SSoT discipline per [[feedback_science_is_ssot_not_project]]. Each operation cites the canonical physics literature — Kepler / Brouwer & Clemence / Murray & Dermott / Smith / Freeth — not any project instantiation. Antikythera-spectral / ephemerides-spectral / chess-spectral are substrate-consumers of these primitives, not their authors.
Changed¶
- ABI stays v2 — three new symbols are pure additions per the Phase B4 convention.
- CMake:
srmech_kepler.cpicked up automatically byfile(GLOB CONFIGURE_DEPENDS c/src/*.c).
Roadmap¶
Phase C1 progress: 13 of 14 classes shipped with C surfaces (A + C from Phase B; I + L + J + B + G + H + D + E + F + N + K from rc1–rc7). Remaining: M (HDC bind/bundle/permute — distributed-representation, rc8).
Per [[feedback_science_is_ssot_not_project]] reframe: the canonical QM/QFT/SM operations layer is being woven into the Phase C1 close rather than deferred to a separate Phase C2 absorption-from-projects pass. Class M rc8 acquires its operational anchor as the binding operation that uncompresses LoE-content along its compression axis per [[user_stance_1d_collapse_to_loe_identity_not_action]] (refined from the prior storage/extraction framing — 1D_t IS the Laws of Everything content, identity; Class M is the substrate-coupling operation, not the dimension itself; see docs/srmech/notes/1d_collapse_to_loe_identity_2026-05-15.md). Canonical single-particle QM operations (TDSE / TISE / Heisenberg / [x̂,p̂] / Liouville-vN / Pauli / hydrogen-radial / harmonic-oscillator) targeted for rc7 follow-up commits or rc8 alongside Class M; sourced from Sakurai / Cohen-Tannoudji / Griffiths.
[0.4.0rc6] - 2026-05-16¶
Added¶
Task #217 Phase C1 — Class N (rational-approximation) C port.
Sixth rc in Phase C1's rc-stacked build-out. Class N is the third pure-integer primitive (after I — modular arithmetic, J — prime factorisation / period). Two operations, both uint64_t, both JPL-clean, both pi-free.
| C symbol | Python wrapper | Operation |
|---|---|---|
srmech_continued_fraction(p, q, terms[], max_terms, *out_count) |
srmech.amsc.rational.continued_fraction(p, q) |
Simple continued-fraction expansion of p/q as [a_0, a_1, ...] via the Euclidean recurrence. |
srmech_best_rational(p, q, max_denom, *out_p, *out_q) |
srmech.amsc.rational.best_rational(p, q, max_denom) |
Best rational p'/q' with q' ≤ max_denom approximating p/q via continued-fraction convergents (Stern-Brocot path through the mediant tree). Overflow-guarded on the convergent recurrence. |
Loop bound SRMECH_RATIONAL_EUCLID_CAP = 128 covers Fibonacci-worst-case for uint64 (~91 iterations). Same constant Class I uses for its Euclidean GCD.
Changed¶
- ABI stays v2 — two new symbols are pure additions per the Phase B4 convention.
- CMake:
srmech_rational.cpicked up automatically byfile(GLOB CONFIGURE_DEPENDS c/src/*.c).
Roadmap¶
Phase C1 progress: 12 of 14 classes shipped with C surfaces (A + C from Phase B; I + L + J + B + G + H + D + E + F + N from rc1–rc6). Remaining: K + M.
- rc7: K (equation-of-centre / pin-slot — orbital arithmetic)
- rc8: M (HDC bind/bundle/permute — distributed-representation)
- 0.4.0 final: clean ship at Phase C1 close
[0.4.0rc5] - 2026-05-16¶
Added¶
Task #217 Phase C1 — Classes D + E + F C ports (real surfaces, not acknowledgment).
Fifth rc in Phase C1's rc-stacked build-out. Three new C primitive operations, one per class, each parity-tested against a pure-Python fallback. Step 1 of C/Python parity per the architectural commitment: every primitive class earns its C surface.
| Class | C symbol | Python wrapper | Primitive operation |
|---|---|---|---|
| D (dispatch) | srmech_dispatch_match |
srmech.amsc.dispatch.match |
Given input bytes + ordered (pattern, tag) rules, return tag of first rule whose pattern occurs in input. Multi-needle pattern dispatcher; builds on Class G's srmech_byte_search internally. |
| E (catalog / naming) | srmech_catalog_lookup |
srmech.amsc.naming.lookup |
Binary search over sorted (key, value) catalog. Lex-comparison with length tiebreak. O(log n) lookup, ≤64 iterations cap. |
| F (template render) | srmech_template_render |
srmech.amsc.template.render |
Render template with {key} placeholders, substituting via key→value catalog (uses Class E's srmech_catalog_lookup internally). |
Naming note: the new Python primitives are srmech.amsc.dispatch / srmech.amsc.naming / srmech.amsc.template to avoid collision with the existing application-layer modules (amsc.catalog is Class E applied to attested-source registries, amsc.descriptor.render_template is Class F applied to descriptor cite/purpose templates). Application modules can later rebuild on the primitive surface for hot-path optimisation.
Changed¶
CLAUDE.md operational-scope-clarification rewritten to remove the "binding-layer concern" framing. Per [[feedback_no_binding_layer_carveout]]: every primitive class earns a C surface; "binding-layer" is not a legitimate skip-class directive. Specific scope-bounded helpers (e.g., TOML parsing stays Python per the Phase B5 vendoring-scope decision) are framed as their own scope concerns, not as class-skipping carve-outs.
ABI stays v2 — three new symbols are pure additions per the Phase B4 convention. CMake picks up srmech_dispatch.c / srmech_catalog.c / srmech_template.c automatically via file(GLOB).
Roadmap¶
Phase C1 progress: 11 of 14 classes shipped with C surfaces (A + C from Phase B; I + L + J + B + G + H + D + E + F from rc1–rc5). Remaining: K + M + N.
- rc6: N (rational-approximation — Stern-Brocot continued fractions; pure integer)
- rc7: K (equation-of-centre / pin-slot)
- rc8: M (HDC bind/bundle/permute)
- 0.4.0 final: clean ship at Phase C1 close
[0.4.0rc4] - 2026-05-16¶
Added¶
Task #217 Phase C1 — Classes B + G + H (the lightweight trio).
Fourth rc in Phase C1's rc-stacked build-out. Bundles three lightweight classes whose primitive operations each fit a small C surface, freeing up rc cadence for the heavier classes (M, K) later.
- Class B (tagged-tuple) —
srmech_tlv_pack(tag, value, value_len, out, capacity, *written)produces deterministic[u8 tag][u32 length BE][value]byte sequences for hashing / fingerprinting typed records. Format is wire-spec ordered (tag-first per the layout); documented as the exception to[[feedback_struct_field_ordering_big_first]]. JSON-record parsing stays Python-side per srmech CLAUDE.md operational-scope-clarification. - Class G (discovery/search) —
srmech_byte_search(haystack, h_len, needle, n_len, *out_offset)finds first occurrence of a byte pattern via naive O(n*m) (fast for srmech's small-haystack cases — descriptor lookups, fingerprint matching). Empty needle matches at offset 0 (matches Python'sbytes.find(b'')). Catalog dictionary lookups stay Python-side. - Class H (self-introspection) — already shipped via
srmech_meta.c'ssrmech_version()andsrmech_abi_version()(Phase B2 baseline). This rc explicitly acknowledges H's mapping to those existing primitives for the cross-substrate-audit roster; no new C symbols added for H.
Public Python surfaces at srmech.amsc.tlv and srmech.amsc.search with native/fallback dispatch. Parity tests at tests/test_lightweight_parity.py cover reference values + Python-equivalence + native↔fallback sweep.
Changed¶
- Class O dissolution (resolution 2026-05-16). The signed-metric / Wick-rotation operation located by Spike #24 bonus 8 and narrowed by bonus 9 was dissolved into Class L as a signed-Laplacian-variant sub-operation per user direction "nothing else so far has been privileged." Vocabulary stays at 14 classes A–N; no Class O added. Future Class L rcs will add the signed-Laplacian op when Phase C2 cascade-composition work calls for it. New memory entry
[[feedback_no_privileged_primitive_classes]]records the design principle: dissolution into existing classes is the default disposition for candidate primitives; promotion requires structural irreducibility. Bonus 11d (Class P sign-rule reduced to existing) is the precedent. - ABI stays v2 — two new symbols (tlv_pack, byte_search) are pure additions per the Phase B4 convention.
- CMake:
srmech_tlv.candsrmech_search.cpicked up automatically byfile(GLOB CONFIGURE_DEPENDS c/src/*.c).
Roadmap¶
Phase C1 progress: 8 of 14 classes shipped (A + C from Phase B; I + L + J + B + G + H from Phase C1 rc1–rc4). Remaining: D / E / F / K / M / N. Class D and Class F are likely Python-only-by-design per srmech CLAUDE.md operational-scope-clarification (binding-layer concerns). Heavier classes (M = HDC bind/bundle, K = equation-of-centre/pin-slot) come later as dedicated rcs.
[0.4.0rc3] - 2026-05-15¶
Added¶
Task #217 Phase C1 — Class J (prime-factorisation / period) C parity.
Third per-class C port in Phase C1's rc-stacked build-out. Class J ("J prime-factorisation/period" in Spike #24's cumulative cross-substrate audit) complements Class I (modular arithmetic) with the non-modular integer-structure operations.
Three new C symbols (all uint64_t, JPL Power-of-Ten clean, no malloc, pi-free):
srmech_is_prime(n, *out)— trial-division primality test (false forn < 2, true for 2 / 3, then test oddd ≤ sqrt(n)).srmech_factor(n, primes[], exponents[], max_count, *out_count)— trial-division prime factorisation returning sorted distinct primes + exponents. Caller-allocated fixed-size buffers;SRMECH_ERR_OVERFLOWif distinct-prime count exceedsmax_count.srmech_cyclic_period(a, n, max_k, *out_period)— multiplicative order ofain(Z/nZ)*via trial-period (smallestk > 0witha^k ≡ 1 mod n). Bounded bymax_k;SRMECH_ERR_OVERFLOWif period exceeds the bound. Requiresgcd(a mod n, n) == 1(validated by detectinga mod n == 0).
Public Python surface at srmech.amsc.primes with native/fallback dispatch. Returns ordinary Python types (bool, list[(int, int)], int) — no numpy dependency at this module level. Parity tests at tests/test_primes_parity.py cover reference values + Python-equivalence on random sweeps + native↔fallback parity.
Foundation for Task #218 Phase C2's cascade-period operations (Class J × Class I composition for cyclic-cascade orbital periods).
Changed¶
- ABI stays v2 — three new symbols are pure additions per the Phase B4 convention.
- CMake:
srmech_primes.cpicked up automatically byfile(GLOB CONFIGURE_DEPENDS c/src/*.c).
[0.4.0rc2] - 2026-05-15¶
Added¶
Task #217 Phase C1 — Class L (graph Laplacian) C parity.
Second per-class C port in Phase C1's rc-stacked build-out. Class L is Spike #24's structural workhorse (instantiated at six of six bonus substrates per the cumulative cross-substrate audit) and the spectral substrate underpinning cascade-composition mass-spectrum reproduction.
Four new C symbols (all uint32/double, JPL Power-of-Ten clean, pi-free per [[user_stance_pi_as_projection]]):
srmech_graph_dense_adjacency—Amatrix from undirected edge list (self-loops add2*wto diagonal per standard convention).srmech_graph_dense_laplacian—L = D − A(combinatorial Laplacian).srmech_graph_normalized_laplacian—L_sym = I − D^(−1/2) A D^(−1/2)(isolated vertices get diagonal 0, not 1).srmech_jacobi_eigvals— symmetric Jacobi eigendecomposition with algebraicc, scomputation (no trig calls). In-place on caller-owned matrix.
N bound: SRMECH_LAPLACIAN_MAX_NODES = 256 caps the stack-allocated degree / row-scaling buffers (~2 KB) for embedded-safe execution. Larger graphs return SRMECH_ERR_OVERFLOW and the Python wrapper falls back to numpy.linalg.eigvalsh.
Public Python surface at srmech.amsc.laplacian (dense_adjacency, dense_laplacian, normalized_laplacian, jacobi_eigvals) with native/fallback dispatch. Parity tests at tests/test_laplacian_parity.py cover reference values, spectral-property invariants (PSD, row-sum=0, normalised eigvals in [0, 2]), and a native↔fallback random sweep.
Pi-free decision: cyclic-graph closed-form spectra (the pi-bearing 2(1−cos(2πk/n)) shortcut) are NOT shipped on the C surface — those are downstream projections of Class I's integer-cyclic upstream. Users computing cyclic-graph spectra compose Class I (modular arithmetic) with Class L's dense build + Jacobi, or use numpy at the Python layer.
Changed¶
- numpy is now a hard runtime dependency (added to
[project.dependencies]). Class L (graph Laplacian) and the upcoming Class M (HDC bind/bundle) are fundamentally array-numerical; numpy provides the ergonomic Python surface + fallback path. Pyodide environments install numpy via micropip.srmech.amsc.cyclic(Class I, integer-only) does not import numpy. - ABI stays v2 — four new symbols are pure additions per the Phase B4 convention.
- CMake:
srmech_laplacian.cpicked up automatically byfile(GLOB CONFIGURE_DEPENDS c/src/*.c)— no CMakeLists edits required. Existing libm linkage covers thesqrtcalls.
Roadmap¶
Phase C1 continues — remaining classes (B/D/E/F/G/H/J/K/M/N + Class O if accepted) ratchet as further rc-stacked additions under 0.4.0rcN. Class D and Class F likely Python-only-by-design per srmech CLAUDE.md operational-scope-clarification. Phase C1 closes at clean 0.4.0.
[0.4.0rc1] - 2026-05-15¶
Added¶
Task #217 Phase C1 — Class I (cyclic-group / modular arithmetic) C parity.
First per-class C port in the post-v0.2.0 Phase C1 build-out (Task #217 follows Task #201 Phase B's ratchet). Class I appears in Spike #24's cumulative cross-substrate audit at five of six bonus substrates (tactical / SHA-256 / MFO 3+7+1 / RNG / cascade composition) and is the foundation primitive for Task #218 Phase C2's cascade-composition operations.
Six new C symbols (all uint64_t, JPL Power-of-Ten clean, no malloc, fixed-bound loops, ≥2 asserts per function):
srmech_gcd(a, b, *out)— Euclidean GCD (gcd(0, 0) = 0).srmech_lcm(a, b, *out)— LCM via GCD withUINT64_MAXoverflow guard.srmech_mod_add(a, b, n, *out)—(a + b) mod n, overflow-safe.srmech_mod_mul(a, b, n, *out)—(a * b) mod nvia russian-peasant doubling (portable; no__int128/_umul128).srmech_mod_pow(a, k, n, *out)—a^k mod nvia square-and-multiply.srmech_mod_inv(a, n, *out)— modular inverse via extended Euclidean (requiresn ≤ INT64_MAXfor int64 intermediate coefficients).
Public Python surface at srmech.amsc.cyclic with native/fallback dispatch (parity tests in tests/test_cyclic_parity.py).
Changed¶
- ABI stays v2. Six new symbols are pure additions per the Phase B4 convention; existing ABI-tied wire formats unchanged.
- CMake:
srmech_cyclic.cis picked up automatically byfile(GLOB CONFIGURE_DEPENDS c/src/*.c)— no CMakeLists.txt edits required. - JPL audit:
srmech_cyclic.cparticipates in the pytest ratchet attests/test_jpl_audit.py(Rules ⅓/⅘/8 mechanically detected).
Roadmap context¶
This release is the start of Task #217 Phase C1's per-class C-parity build-out. Phase C1 ratchets remaining primitive classes (B/D/E/F/G/H/J/K/L/M/N + Class O if accepted) as rc-stacked additions under 0.4.0rcN per [[feedback_rc_stacking_versioning]], with the clean 0.4.0 ship at Phase C1 close. Class D and Class F are likely Python-only-by-design (binding-layer per srmech CLAUDE.md operational-scope-clarification); each class gets a per-port decision recorded in CLAUDE.md.
Phases C2 (Task #218 — MFO/SM/QM operations layer), C3 (Task #219 — per-class CLI help-arg discipline), and C4 (Task #220 — tool-schema extension for catalog files) build on Phase C1's foundation.
[0.3.1] - 2026-05-14¶
Production cut bundling rc1 + rc2 (no code change from 0.3.1rc2)¶
The 0.3.1rc2 → 0.3.1 transition contains only version-string bumps in the four SSOT locations plus this CHANGELOG header. Bundles both POC findings from the chess-spectral simple-profile migration (Task #211):
- rc1: entry-point Form-1 (package-only) support — every
real-world Python plugin discovery system uses
"package_name"rather than"package:CONST". - rc2:
[profile.tool_schema].extension_filewas parsed at validation time but never loaded at activation time, so profile tool entries silently went missing from the registry.
Both fixes are backward-compatible additions to the loader; no v0.3.0 API breakage.
End-to-end verification (Windows / Python 3.14, clean venv, TestPyPI 0.3.1rc2 + chess-spectral 1.19.0 pre-release wheel):
srmech version: 0.3.1rc2
=== chess profile activation ===
Profile: chess v1.19.0
=== tool_schema integration ===
chess tools registered: 8
- chess.encode_2d : Spectral 2D chess encoder...
- chess.encode_4d : Spectral 4D chess encoder...
- chess.fen_to_pos : Parse a FEN string into the 2D position dict...
- chess.channel_energies : Compute per-channel L² energy...
- chess.encode_2d_pure_phase : Integer-arithmetic 2D chess encoder...
- chess.phase_only_pseudo_legal_moves : Pure-phase pseudo-legal...
- chess.encode_2d_bip_hybrid : BIP-hybrid sign × magnitude...
- chess.decode_2d_bip_hybrid : Inverse of encode_2d_bip_hybrid...
=== bridge call still works ===
encode_2d shape: (640,)
Profile pattern (ADR-0001) is now exercised by a real third-party-style package. ADR §7 Step 1 (chess POC) drives ADR §7 Step 2 (ephemerides plugin-profile) next.
See [0.3.1rc2] + [0.3.1rc1] below for the full bug + fix narratives.
[0.3.1rc2] - 2026-05-14¶
Fixed — [profile.tool_schema] extension file loading¶
Second issue surfaced by the chess-spectral simple-profile POC (Task #211).
v0.3.0–v0.3.1rc1: the profile loader's _validate_descriptor accepted
[profile.tool_schema] blocks at parse time, but Profile.__init__
never actually loaded the referenced extension TOML at activation
time. Profiles declaring tool-schema extensions activated cleanly but
contributed zero ToolEntry records to srmech.amsc.tool_schema.
Repro (v0.3.1rc1 against chess-spectral 1.19.0):
>>> import srmech
>>> p = srmech.profile("chess") # activates cleanly
>>> from srmech.amsc.tool_schema import get_tool_schema
>>> get_tool_schema().by_owner("chess")
[] # ← should have been 8 entries from chess-spectral's
# _srmech_tool_schema.toml
Fix in Profile.__init__: new _load_tool_schema_extension() step.
After bridge resolution + catalog registration + native plugin
loading, if [profile.tool_schema].extension_file is declared, the
loader resolves it inside the package directory and registers every
[[tools]] block via srmech.amsc.tool_schema.register_profile_tools()
with owner = profile.name.
Verified against chess-spectral's 8-tool extension file
(_srmech_tool_schema.toml):
chess tools registered: 8
- chess.encode_2d : Spectral 2D chess encoder...
- chess.encode_4d : Spectral 4D chess encoder...
- chess.fen_to_pos : Parse a FEN string into the 2D position dict...
...
Backward-compatible: profiles without a [profile.tool_schema] block
take the no-op path. Profiles with malformed extension files raise
InvalidProfileError at activation time, before any bridge surface
is bound — fail-loud-at-boot per ADR-0001 §5.5.
[0.3.1rc1] - 2026-05-14¶
Fixed — entry-point Form-1 (package-only) support for profile loader¶
v0.3.0's _resolve_entry_point_toml only handled "package:CONST"
attribute-style entry-point declarations (Form 2). Every real-world
Python plugin discovery system (pytest, flake8, setuptools_scm) uses
the simpler "package_name" form — and that's what surfaced
immediately during the chess-spectral simple-profile POC migration
(Task #211,
ADR-0001 §7 Step 1).
Symptom (v0.3.0 against chess-spectral 1.19.0's declaration):
>>> import srmech
>>> srmech.list_profiles()
{'chess': ProfileStatus(name='chess', ..., status='invalid',
diagnostic="entry-point 'chess' ('chess_spectral') resolved to
module; expected Path or str pointing at srmech_profile.toml")}
Fix in srmech/profile_loader.py:
_resolve_entry_point_toml now handles three entry-point value
forms:
- Package only (recommended, boilerplate-free):
chess = "chess_spectral". Loader usesimportlib.resources.files(package) / "srmech_profile.toml". - Path/str attribute (explicit, v0.3.0 form):
chess = "chess_spectral:_SRMECH_PROFILE_PATH". Unchanged. - Callable returning a Path/str (for descriptors generated at
import time):
chess = "chess_spectral:_get_path". Unchanged in intent — was always documented as supported but never wired.
Backward-compatible: every v0.3.0 caller continues to work; Form 1 is purely additive.
ADR-0001 §7 Step 1 explicitly anticipated this kind of finding:
"Lessons learned go back into the ADR + the §3 schema if needed."
The schema doesn't change; only the loader implementation. The authoring guide (Task #214) will recommend Form 1 as canonical.
Tests¶
- New:
test_entry_point_form_1_package_only(synthesised package on tmp_path; verifiesimportlib.resources.files()resolution). - New:
test_entry_point_form_2_path_constant(Form 2 regression). - New:
test_entry_point_form_2_string_constant(Form 2 regression). - New:
test_entry_point_form_3_callable_returning_path. - New:
test_entry_point_unknown_type_rejected(negative case).
Why a patch bump (not minor)¶
The fix is purely additive to the loader's accepted inputs. v0.3.0's documented behaviour (Form 2) continues to work identically. No new public surface, no API breakage. SemVer patch is correct.
[0.3.0] - 2026-05-14¶
Production cut of the v0.3.0 ship (no code change from 0.3.0rc1)¶
The 0.3.0rc1 → 0.3.0 transition contains only version-string bumps
in the four SSOT locations (pyproject.toml, pyproject-pure.toml,
srmech/version.py, c/include/srmech.h) plus this CHANGELOG header.
TestPyPI verification (clean venv, Windows / Python 3.14):
version: 0.3.0rc1
HAS_NATIVE: True ABI: 2
tool_schema_version: 1.0
builtin tools: 6
list_profiles: {}
ProfileNotFoundError works: no profile named 'nonexistent'; enumerated profiles: []
All profile loader exports present: True
Native dispatch healthy, builtin AMSC tools self-register at amsc import time, profile loader API complete. No issues surfaced through the rc cycle; cutting straight to production.
See [0.3.0rc1] below for the full feature description.
[0.3.0rc1] - 2026-05-14¶
Added — Task #198 (srmech.amsc.tool_schema) + Task #199 (profile loader)¶
First implementation of the profile pattern specified in ADR-0001. Ships as v0.3.0rc1 to TestPyPI for verification before the production v0.3.0 cut.
srmech.amsc.tool_schema — LLM-friendly introspection (Task #198)¶
New module that produces a single structured view of every callable srmech exposes (and, post-profile-pattern, every profile-contributed callable). API:
get_tool_schema()— returns aToolSchemadataclass with every registeredToolEntry. JSON-serialisable via.to_jsonable().tool_schema_view()— convenience wrapper returning the same as a dict.register_tool(entry)— imperative registration; idempotent on identical re-registration; raisesToolSchemaConflictErroron name collision with different content.register_profile_tools(profile_name, entries)— batch path used by the profile loader; enforcesentry.owner == profile_nameso profile-attribution can't drift.unregister_profile_tools(profile_name)— removes every entry owned by the named profile (used on profile deactivation).load_extension_file(path, owner)— parses a profile's TOML extension file into a list ofToolEntryready for batch registration.
srmech's own AMSC functions (sha256_bytes, read_ndjson, descriptor_hash, list_attested_sources, get_attested_dataset, register_attested_root) are registered at AMSC import time with their parameter signatures, return shapes, and smoke-test hints.
srmech.profile_loader — profile activation API (Task #199)¶
New module implementing ADR-0001's profile pattern:
srmech.list_profiles()— enumerates every installed profile viaimportlib.metadata.entry_points(group="srmech.profiles"). Eager at first call per ADR §5.5 (JPL Rule 2 analog); cached for process lifetime.srmech.profile(name)— activation API. Returns aProfileobject exposing bridge surfaces as attributes. On first call for a given profile-version:- Validates the descriptor against the v1.0 schema (strict).
- Checks smoke-test cache at
~/.cache/srmech/profile_smoke_tests/<name>-<version>.toml. - Cache miss / version bump → re-runs smoke test (bridge surfaces importable + callable; catalog roots exist).
- On smoke-test pass: registers catalog roots into srmech's
universal bridge; loads native plugin via ctypes if
[profile.native]declared, performs ABI handshake; caches result; returnsProfile. - On smoke-test fail: raises
SmokeTestFailedError; profile not activated; cache records the failure (re-runs on next process). Profile.<bridge_surface>(args)— invoke a profile-declared bridge function.Profile.native— bound ctypes library (plugin tier only).
Error hierarchy:
ProfileError ⊂ Exception
- ProfileNotFoundError — unknown profile name
- InvalidProfileError — descriptor failed validation
- ProfileSchemaVersionError — descriptor against unknown schema version
- SmokeTestFailedError — smoke test failed (cache may record)
- AbiMismatchError — plugin's abi_version() mismatch
JSON Schema for srmech_profile.toml¶
docs/srmech/adr/0001-profile-pattern.schema.json
renders ADR §3 into a machine-checkable shape. The loader uses a
pure-Python minimal validator that covers the load-bearing
constraints (required fields, name/version patterns, schema-version
match); the full JSON Schema is the documented source-of-truth for
profile authors and for third-party validation tools.
[profile.interpreted] is reserved (ADR §5.6)¶
Profiles declaring an [profile.interpreted] block (Julia / R / Lua /
subprocess runtimes) parse cleanly but emit a FutureWarning and
the block is ignored. The namespace is reserved in v1.0 of the
schema so adding interpreted-runtime adapters later (a follow-up
ADR) won't be a breaking change.
Tests¶
tests/test_tool_schema.py(NEW) — 11 tests covering imperative + extension-file registration, idempotency, conflict detection, owner-tag enforcement, by_owner filter, lookup, serialisation round-trip.tests/test_profile_loader.py(NEW) — 14 tests covering schema validation paths (minimal valid; missing fields; bad patterns; full plugin-tier[profile.native]; reserved[profile.interpreted]block warns), public API surface, and error-class exports.
All v0.2.0 tests (sha256 parity, NDJSON parity, JPL audit ratchet, etc.) continue to pass unchanged.
Version¶
This is a minor bump (0.2.0 → 0.3.0). Adds new APIs; no breaking changes to v0.2.0's public surface. C ABI still 2.
[0.2.0] - 2026-05-14¶
Task #201 Phase B7 — production cut to PyPI¶
First production PyPI release of native-C-accelerated srmech.
Content is functionally identical to 0.2.0rc2 on TestPyPI;
only the version string changes (rc-suffix stripped) and the
docs lose the rc-cycle commentary. The tag-routing claim in
srmech-publish.yml directs a non-rc tag to the production PyPI
trusted-publisher environment.
What v0.2.0 ships, headline¶
The Task #201 build-out (rc3 → rc9 + rc1 → rc2 = 11 TestPyPI rcs across phases B1 through B7) turned srmech from a pure-Python AMSC framework (the v0.1.0 ship) into a native-C-accelerated multi-platform package at peer quality with ephemerides-spectral:
- Native C library (
srmech_sha256_hex,srmech_ndjson_iter, - version / ABI accessors) shipped under
srmech/_native/inside platform-tagged wheels. - 15-cell cibuildwheel matrix — Linux (manylinux_2_28) × macOS
× Windows × py3.10 / 3.11 / 3.12 / 3.13 / 3.14. Each cell runs
test_native_sha256.py+test_format.pyto verify the wheel's native dispatch + sha256 parity post-build. - scikit-build-core + CMake build backend (Phase B2). Pure-
Python fallback for Pyodide / WASM lives in
pyproject-pure.toml(hatchling backend, swapped in for thebuild-pure-wheelCI job). - All
hashlib.sha256callsites insrmech.amscroute throughformat.sha256_bytes()→ native dispatch when available; hashlib fallback otherwise. - JPL Power-of-Ten audit complete (Phase B6). 10/10 rules
satisfied modulo one documented Rule 9 callback deviation; ratchet
enforced by
tests/test_jpl_audit.py(6 mechanical tests, pinned exemption list) +pedantic-buildCI job (3-cell: Linux gcc / macOS clang / Windows MSVC ×-DSRMECH_PEDANTIC=ON→-Werror//WX). - Description-match guard between
pyproject.tomlandpyproject-pure.toml(rc9 post-mortem). Both descriptions carry the same 450-char Summary: "Stored-Relationship Mechanism research package: home of the Attested Multi-Source Collector/Catalog (AMSC) framework — ...". - AMSC dual-name framing (rc2). Both Collector (at fetch time) and Catalog (at read time) work; same abbreviation; pick whichever fits the lifecycle stage.
- Development Status classifier bumped
3 - Alpha→4 - Beta(rc9).
Cross-package readiness¶
ephemerides-spectral 0.26.1rc1 (the parallel-session ship) pins
srmech>=0.1.1rc9 with a TestPyPI PIP_EXTRA_INDEX_URL override
to exercise the cibuildwheel matrix against the TestPyPI srmech
rcs. With v0.2.0 now on production PyPI, the next
ephemerides-spectral release will bump that floor to
srmech>=0.2.0 and drop the TestPyPI override.
v0.1.0 status¶
Still on PyPI as the historical release. pip install srmech
without any version constraint now resolves to v0.2.0; users on
older Python paths can still pin srmech==0.1.0 for the
pure-Python wheel.
History¶
See the rc-by-rc entries below for the full per-phase record:
0.2.0rc2— AMSC "Collector/Catalog" dual-name wording0.2.0rc1— Phase B7 final TestPyPI gate (no-op version bump from rc9)0.1.1rc9— Metadata drift sweep ("Pure Python." → "Native C dispatch"; Dev Status 3-Alpha → 4-Beta; description-match guard)0.1.1rc8— Phase B6 JPL Power-of-Ten audit + ratchet0.1.1rc7— Phase B5 sha256 callsites routed through native0.1.1rc6— Phase B4 NDJSON streaming reader C port0.1.1rc5— Phase B3 SHA-256 C port + cibuildwheel matrix0.1.1rc4— Phase B2 scikit-build-core + pyproject-pure0.1.1rc3— Phase B1 C tree scaffolding0.1.1rc1/rc2— Earlier infrastructure cycles0.1.0— Initial AMSC-to-srmech refactor (pure-Python)
[0.2.0rc2] - 2026-05-14¶
Added — Task #201 Phase B7: AMSC dual-name wording ("Collector / Catalog")¶
Documents the dual reading of the AMSC abbreviation across srmech's user-facing surface. No code, no API, no ABI change — pure documentation polish discovered while reviewing the 0.2.0rc1 TestPyPI metadata.
The framing¶
AMSC abbreviates both:
- Attested Multi-Source Collector — at collection time (T1 fetch / T3 live query / re-bake lifecycle stages), the framework's adapter classes are collecting attested rows from upstream archives.
- Attested Multi-Source Catalog — after collection, the committed NDJSON SSOTs constitute a catalog of attested data that downstream packages register and query through the universal bridge.
Both names are correct; both abbreviate to AMSC; pick whichever fits the lifecycle stage you're describing. One framework wearing two hats.
Surfaces updated¶
pyproject.toml+pyproject-pure.toml[project].description— "Attested Multi-Source Collector (AMSC)" → "Attested Multi-Source Collector/Catalog (AMSC)". 442 chars → 450 chars (still under both the 480 soft cap and PyPI's 512 hard cap).python/README.md— package-intro paragraph updated; new "Why 'Collector/Catalog'?" subsection explains the dual reading with the T1/T3-fetch vs read-time-query lifecycle framing.python/srmech/__init__.pydocstring — package-level framing now leads with the dual name and gives a paragraph on the lifecycle-stage interpretation.python/srmech/amsc/__init__.pydocstring — same dual- name framing at the AMSC subpackage level.docs/srmech/srmech_research_notebook.md§0 — three-layer architecture's L1 paragraph gains a "Naming aside" note introducing both readings, with explicit lifecycle-stage cross-references (list_attested_sourcesetc.).docs/srmech/CLAUDE.mdstate snapshot bumped to reflect the rc2 ship.
Why TestPyPI rc rather than land-as-unreleased¶
Initial intent (per maintainer's "leave this as an unreleased
update" guidance) was to land the doc change on main without a
new rc; but per the project's TestPyPI-before-PyPI discipline,
any text that goes to production PyPI's Summary metadata should
have been visible on TestPyPI first. PyPI Summary drift (the
"Pure Python." bug at rc8 → rc9) was the specific failure mode
that motivated the description-match guard; landing the dual-name
wording without a TestPyPI round-trip would re-open the same
exposure. So we ship rc2 to TestPyPI and verify there, then v0.2.0
(no rc suffix) cuts to production PyPI carrying the rc2 text.
No code change¶
C ABI still 2. Python public API surface unchanged. Wheel
content identical to rc1 modulo the description string +
docstrings. Pytest matrix unaffected (the
test_native_version_and_abi rc9-bump fix from rc1 keeps working).
[0.2.0rc1] - 2026-05-13¶
Task #201 Phase B7 — final TestPyPI rc before v0.2.0 production cut¶
No code changes from 0.1.1rc9. This release exists to validate
the v0.2.0 version string itself through one more TestPyPI
round-trip before the clean srmech-v0.2.0 tag goes to
production PyPI. Discipline: TestPyPI before PyPI, always —
the rc-suffix auto-routing in srmech-publish.yml means a clean
non-rc tag IS the production gate; we want one last sanity
verification on the version string + metadata immediately before
the gate-passing tag.
Why a minor bump (0.1.1 → 0.2.0)¶
The rc3 → rc9 series turned srmech from a pure-Python AMSC
framework into a native-C-accelerated package with cibuildwheel
matrix + JPL Power-of-Ten audit + per-platform parity tests
covering 3 OS × 5 Python versions. That's a real capability
boundary, large enough that consumers of srmech==0.1.0
upgrading via pip install -U srmech are going on a substantive
ride. Minor bump signals that.
Cross-package readiness (parallel session shipped this)¶
While the srmech rc series was iterating, a parallel Claude
Code session verified srmech rc9 against the sister package
ephemerides-spectral (which depends on srmech as its AMSC
substrate per Task #197). The verification result lives at
docs/antikythera-maths/ephemerides-spectral/CHANGELOG.md
under ephemerides-spectral 0.26.1rc1. That rc shipped to
TestPyPI with srmech>=0.1.1rc9 pinned + a
PIP_EXTRA_INDEX_URL=https://test.pypi.org/simple/ test-env
override (Option B from the verification prompt), confirming
the cibuildwheel test matrix actually exercises against the
TestPyPI srmech rc rather than silently falling back to PyPI's
srmech==0.1.0. Cross-package integration confirmed green.
After srmech v0.2.0 ships to production PyPI, ephemerides-spectral
will bump its srmech floor >=0.1.1rc9 → >=0.2.0 and drop the
TestPyPI test-env override in its own follow-up release. That's
ephemerides-spectral's ship to plan, not srmech's.
Path forward¶
- This rc1 auto-ships to TestPyPI via the rc-suffix routing.
- Maintainer verifies wheel install + native dispatch + sha256 parity + ndjson parity end-to-end from a clean venv outside the repo tree.
- If clean, maintainer bumps
0.2.0rc1→0.2.0(drop thercNsuffix in all four SSOT files), merges that bump, and tagssrmech-v0.2.0. That clean tag auto-routes to production PyPI via the workflow's environment-name claim. - After v0.2.0 lands on PyPI, ephemerides-spectral can bump
its srmech floor; downstream consumers can upgrade via
pip install -U srmech.
No ABI / API / behaviour change¶
C ABI version unchanged (still 2). Python public surface
unchanged. Wheel content identical to rc9 modulo the version
string. The SRMECH_VERSION macro updates in lockstep
(0.1.1rc9 → 0.2.0rc1) and the Python _native.py reads it
back through srmech_version() at load time.
[0.1.1rc9] - 2026-05-13¶
Fixed — PyPI metadata drift after Phase B3 (native code) landed¶
User-spotted drift on the TestPyPI project page: the Summary still
read "...Pure Python." even though Phase B3 (rc5) shipped native C
dispatch and Phase B4 (rc6) added the second native symbol. Both
pyproject.toml and pyproject-pure.toml had the stale claim
verbatim because the description text was copy-pasted between them
without revisiting the trailing sentence after each phase.
Fixed¶
pyproject.toml+pyproject-pure.toml[project].description— replaced "Pure Python." with "Native C dispatch (SHA-256 + NDJSON line reader) with pure-Python fallback for Pyodide / WASM." Both files now carry identical 442-char descriptions (well under the 480-char soft cap; well under PyPI's 512-char hard limit).README.mdStatus line — refreshed to reflect the rc3→rc8 arc and the impending v0.2.0 cut. Adds a one-liner clarifying the native-C + pure-Python-fallback architecture in the package intro paragraph.Development Statusclassifier — bumped from3 - Alpha→4 - Betaon both pyproject files. After 6 rc iterations including cibuildwheel matrix, JPL Power-of-Ten audit, Python/C parity tests, and pedantic-build CI on three platforms, "Beta" is the honest label. Same status ephemerides-spectral carries.
Added — description-match guard (defensive ratchet)¶
The publish workflow (srmech-publish.yml) and CI workflow
(srmech-ci.yml) already enforce version-match between
pyproject.toml and pyproject-pure.toml. The same guard pattern
now also asserts description-match: any drift between the two
descriptions fails CI with a clear error message including both
char counts. This catches future copy-paste drift before it can
reach a TestPyPI / PyPI upload.
PyPI's Summary metadata is per-project-version (not per-wheel), so both wheels uploaded under the same version must carry the same Summary text. The match guard formalises that invariant.
Audit scope¶
Reviewed every user-facing PyPI metadata surface for similar drift:
- ✅
description— fixed (both files). - ✅
Development Statusclassifier — bumped. - ✅ README Status line — refreshed.
- ✅
keywords— accurate (stored-relationship, mechanism, attested, provenance, ndjson, ground-proof, research). No change. - ✅
Topic :: Scientific/Engineeringclassifier — accurate. - ✅
Programming Language ::classifiers — matchrequires-python. - ✅
[project.urls]— Homepage, Repository, Issues, Changelog, Notebook. Stable, no drift. - ✅ Docstrings in
_native.py/format.py/c/README.mdthat mention "pure-Python" — all referring to the fallback path correctly; no drift.
No ABI change¶
C surface unchanged from rc8. SRMECH_ABI_VERSION stays at 2.
[0.1.1rc8] - 2026-05-13¶
Added — Task #201 Phase B6: JPL Power-of-Ten audit¶
Formal audit of srmech's native C library against Holzmann's JPL Power-of-Ten rules. Mirrors the pattern ephemerides-spectral applied via Tasks
105–#110. All ten rules satisfied for srmech's C surface,¶
modulo one documented Rule 9 deviation (callback-based iterator).
Audit deliverables (docs/srmech/c/JPL_AUDIT.md)¶
- Rule-by-rule compliance review across all 3 C source files
(
srmech_meta.c,srmech_sha256.c,srmech_ndjson.c) + the public headersrmech.h. ~500 LOC total. - Per-function line + assertion counts with explicit exemption
policy for trivial accessors (
srmech_version,srmech_abi_version) andstatic inlinearithmetic primitives (sha256 bit-rotation helpers). - Rule 9 deviation rationale documented: the
srmech_ndjson_itercallback is the smallest API surface satisfying Rules 3 + 4 simultaneously.
Code fix shipped in this audit pass¶
srmech_ndjson_iterat rc6 was 76 lines (Rule 4 violation:60 lines). The chunk-byte-loop body extracted into a new
static srmech_ndjson_process_chunkhelper along its natural state-update seam. Post-refactor: 51-lineiter+ 43-lineprocess_chunk. Byte semantics identical; 18 ndjson parity tests re-ran clean.
Tests + CI ratchet¶
tests/test_jpl_audit.py(NEW) — 6 mechanically-detectable ratchet tests:- Rule 1: no
goto/setjmp/longjmpanywhere. - Rule 3: no
malloc/calloc/realloc/free/alloca. - Rule 4: every function ≤ 60 lines (line-count regex + brace- depth scanner).
- Rule 5: every non-exempt function has ≥ 2 assertions. Exempt list pinned (8 entries: 2 trivial accessors, 6 inline helpers); adding to the exempt list requires documenting rationale in JPL_AUDIT.md AND updating the test.
- Rule 8: no multi-line macros / token-paste /
__VA_ARGS__. - Audit doc present-and-mentions-all-rules sanity check.
.github/workflows/srmech-ci.ymlgains apedantic-buildjob (3-cell matrix: Linux gcc / macOS clang / Windows MSVC) that runscmake -DSRMECH_PEDANTIC=ON→ builds with-Werror(POSIX) or/WX(MSVC). Any new warning fails CI. Rule 10 toolchain-side enforcement.- All 100 existing tests still pass; pytest collects 106 tests + the JPL ratchet's 6 = 112 total Python tests.
Verification (local)¶
gcc -std=c11 -Wall -Wextra -Wpedantic -Werror -O2builds all 3 C files clean.pytest tests/test_jpl_audit.py→ 6/6 pass.- Full pytest suite (rc8 wheel install) → 106 passed + 1 skipped (1 native-dispatch skip when run from source tree).
Phase plan progress¶
| B1 | C tree scaffolding (rc3) | ✅ | | B2 | scikit-build-core + pyproject-pure (rc4) | ✅ | | B3 | SHA-256 + cibuildwheel matrix (rc5) | ✅ | | B4 | NDJSON streaming reader (rc6) | ✅ | | B5 | Route remaining sha256 callsites (rc7) | ✅ | | B6 | JPL Power-of-Ten audit (rc8) | this ship | | B7 | v0.2.0rc1 final TestPyPI verify → v0.2.0 to PyPI | next |
[0.1.1rc7] - 2026-05-13¶
Changed — Task #201 Phase B5: route remaining sha256 callsites through native dispatch¶
Phase B5's nominal title was "TOML canonical-serialization C port".
The shipped scope is narrower and better-fit: the actual hot work
(SHA-256 over canonicalised bytes) already has a native C path
from Phase B3. B5 routes the four remaining hashlib.sha256
callsites in srmech through sha256_bytes so every per-row
attestation hash benefits from the native dispatch.
Vendoring a TOML parser in C — the original phase plan's
implication — was rejected. CPython's tomllib + json.dumps
canonicalisation is small, fast, and well-tested; replicating it
in C would 3× srmech's native-code surface area for no measurable
gain on the inputs srmech actually processes.
Wired callsites¶
descriptor.descriptor_hash— the load-bearing one. Used by every adapter'sattest()step to computecollector_descriptor_hashper row.catalog._file_sha256— hashes overlay NDJSON files for T2 user-runtime-kernel attestation. Small files (< few MB), so slurp-and-hash viasha256_bytesis fine; streaming hashlib (which we'd need for huge files) would require a separate C-side multi-update API not yet ported.catalog._kernel_cache_hash— cache-key hash over the registered T2 overlay summary.adapters._base.parser_rule_hash— per-row attestation field documenting the parse-section rules.
What stays in Python¶
- TOML parsing (
tomllib.loads) — stdlib, already C-accelerated. - Canonical JSON serialisation (
json.dumps(sort_keys=True, ...)) — stdlib, already C-accelerated. - Streaming hashlib for the (currently unused) very-large-file case.
Tests¶
tests/test_native_descriptor_hash.py(NEW) — 7 parity tests:- 3 descriptor-shape fixtures (minimal, comments + odd-spacing,
deeply-nested keys) comparing native-routed
descriptor_hashto a pure-Python hashlib reference computation. catalog._file_sha256parity vs streaming hashlib.adapters._base.parser_rule_hashparity vs hashlib.- Defensive ratchet asserting all four wired callsites resolve to
the same native path (catches accidental re-introduction of
direct
hashlib.sha256calls). - Full pytest suite (100 tests + 1 skip) all green under native wheel install on Windows MSVC + Python 3.14.
No ABI change¶
C surface area unchanged from rc6. SRMECH_ABI_VERSION stays at 2.
[0.1.1rc6] - 2026-05-13¶
Added — Task #201 Phase B4: NDJSON streaming reader C port¶
Second C/Python parity surface. Native srmech_ndjson_iter does
file-IO + line tokenisation in C; JSON parsing stays in Python.
Byte-exact line-set agreement pinned by the new pytest parity
suite in tests/test_native_ndjson.py (18 tests including
chunk-boundary span + max-line-overflow + CRLF / mixed-EOL fixtures).
C side (docs/srmech/c/)¶
src/srmech_ndjson.c(NEW) — streaming line reader. Reads 64 KiB chunks viafread; assembles partial lines into a static 1 MiB buffer (single-thread contract); invokes the caller's callback with(line, line_len, lineno, user)per non-empty line. Empty lines are silently skipped butlinenostill advances, so callback-side error messages line up byte-exactly with the file (verified bytest_read_ndjson_malformed_line_lineno_correct). CR-stripping at line boundaries matches Python'sraw.rstrip("\r\n").include/srmech.h— callback typedef gainssize_t linenoparameter;SRMECH_ABI_VERSIONbumped to 2.src/srmech_meta.c—srmech_abi_version()now returns the macro indirectly so a missed manual bump can't silently lie.
Python side (docs/srmech/python/srmech/amsc/)¶
_native.py—EXPECTED_ABI_VERSION = 2(matches C-side bump)._NDJSON_LINE_CB— ctypesCFUNCTYPEmirroring the 4-argument C callback typedef.ndjson_lines_c(path) -> list[(lineno, bytes)]— Python wrapper that runs the native iterator under a ctypes callback and collects(lineno, line_bytes)tuples.NativeNDJsonError— distinct fromMPRValidationErrorbecause the failure is upstream of JSON parsing (file IO or overflow). Translated toOSErrorat theformat.read_ndjsonboundary so callers see consistent semantics.format.py—read_ndjson()dispatches via the native iterator whenHAS_NATIVEis True; pure-Python streaming path remains unchanged. JSON parsing (json.loads+MPRRecord.from_json_line) stays in Python on both paths.
Tests¶
tests/test_native_ndjson.py(NEW) — 18 parity tests: 12 fixture inputs (empty file, no-trailing-newline, CRLF / mixed-EOL, blank-line patterns, long lines, 100-record stress, etc.) + theformat.read_ndjsondispatch test + lineno-fidelity test + missing-fileOSErrortest + 1000-record stress + chunk- boundary span test +SRMECH_ERR_OVERFLOWtest (1.25 MiB line rejection).- All 59 existing tests still pass; all 18 native-sha256 tests still pass (ABI v2 lift didn't break the v1 surface).
Notes on design¶
- No JSON parsing in C. srmech's hot path is the file-IO + line
tokenisation overhead (Python's text-mode line iteration has
per-line allocator pressure that adds up across thousand-row
catalogs). Doing the JSON parse in C would need a vendored JSON
parser; bytes returned to Python and parsed via
MPRRecord.from_json_lineis byte-equivalent and avoids that surface-area expansion. - Static 1 MiB line buffer. Trade-off:
srmech_ndjson_iteris not thread-safe. The two callsites today (Pythonformat.read_ndjsonand any future C-side parity test) are serial. Phase B6 audit may revisit, but for srmech's data-pipeline workload — read a catalog file once, iterate — single-thread is the correct model. - Eager line collection. The native path returns a list rather
than a generator. For the catalog files srmech actually reads
(small, few KB to a few MB), the eager materialisation is fine.
If a future use case wants a true generator, the callback can be
wired to a
queue.Queue+ worker thread, but we're not paying that complexity until a real need surfaces.
[0.1.1rc5] - 2026-05-13¶
Added — Task #201 Phase B3: SHA-256 C port (first native symbol)¶
First C/Python parity surface in srmech. Native srmech_sha256_hex
replaces hashlib.sha256 on the hot path used by every adapter's
attest() step. Byte-exact agreement pinned by the new pytest
parity suite in tests/test_native_sha256.py (18 tests) plus the
C-side smoke tests in c/test/test_srmech_sha256.c (12 assertions
against FIPS 180-4 fixtures + padding-boundary edge cases).
C side (docs/srmech/c/src/)¶
srmech_sha256.c— self-contained SHA-256 (FIPS 180-4). No OpenSSL / libcrypto dependency. ~200 lines, JPL-Power-of-Ten- compatible (bounded loops, no malloc, no goto, ≥2 asserts/fn). Public entry:srmech_sha256_hex(data, data_len, out_hex).srmech_meta.c—srmech_version()+srmech_abi_version()metadata accessors. Called by the Python ctypes shim at load time to verify ABI agreement before binding.
The header (docs/srmech/c/include/srmech.h) grows
SRMECH_ABI_VERSION = 1 and declarations for the three new
symbols.
Python side (docs/srmech/python/srmech/amsc/)¶
_native.py(NEW) — ctypes wrapper mirroringephemerides_spectral/_native_bip.py:HAS_NATIVEboolean — guards every callsite.- ABI-version check at load time; mismatch falls back to Python silently (LOAD_ERROR is populated).
- Three-strategy library discovery:
srmech.__path__walk, relative-to-module-file,importlib.metadata.files()fallback. The third strategy is load-bearing for scikit-build-core editable installs where the .py files live in the source tree but the CMake-installed .so/.dll/.dylib lives in site-packages. sha256_hex_c(data) -> str— native entry. Handles empty bytes correctly (mirrors hashlib.sha256(b"") semantics).format.py—sha256_bytes()now dispatches to native when available, falls back tohashlibotherwise. The user-facing API is unchanged; the implementation is one branch deeper.
Tests¶
tests/test_native_sha256.py(NEW) — 18 parity tests: 15 fixture inputs (empty, FIPS B.2, B.3, padding boundaries at 55/56/63/64/65/119/128 bytes, 1 KiB, 64 KiB, 256 KiB),format.sha256_bytesdispatch test, version/ABI lock test, 200-input randomised parity test. Auto-skipped whenHAS_NATIVEis False (pure-Python wheel / Pyodide install).c/test/test_srmech_sha256.c(NEW) — 12 C-side asserts against FIPS 180-4 vectors + padding edge cases. Exits 0 on all-pass.
Build¶
pyproject.toml— Phase B2'swheel.py-api = "py3"+wheel.platlib = falseoverrides REMOVED. The wheel is now legitimately platform-tagged (e.g.srmech-0.1.1rc5-cp312-cp312-linux_x86_64.whl) and containssrmech/_native/libsrmech.{so,dll,dylib}..github/workflows/srmech-publish.yml—build-wheelsanity check inverted: rejects py3-none-any output (would indicate CMake short-circuited and the .so is missing), requiressrmech/_native/to contain a .so / .dll / .dylib in the wheel.
Phase B7 follow-up¶
The build-wheel job still runs on a single Ubuntu cell, so only
the Linux wheel is published at rc5. Mac / Windows users on TestPyPI
get the pure-Python wheel (built by build-pure-wheel) and the
pure-Python hashlib fallback. Phase B7 adds the cibuildwheel
matrix that produces wheels for all platform/Python combinations.
[0.1.1rc4] - 2026-05-13¶
Infrastructure — Task #201 Phase B2: scikit-build-core + pyproject-pure swap¶
Switches srmech's build backend from hatchling to scikit-build-core +
CMake, mirroring ephemerides-spectral. Adds the
pyproject-pure.toml hatchling-fallback file for the Pyodide / WASM
build path. Rewrites srmech-publish.yml with the three-job shape
(scikit-build-core wheel + sdist + pure-Python wheel) that mirrors
ephemerides-spectral-publish.yml.
Phase B2 still ships py3-none-any wheels — until Phase B3 lands
real C code in docs/srmech/c/src/, the CMake step short-circuits
to "no library" and the wheel is tagged py3-none-any via the
wheel.py-api = "py3" + wheel.platlib = false overrides in
pyproject.toml. Both overrides come back OUT at Phase B3 so the
wheel becomes legitimately platform-tagged once the native binary
is real.
Added — pyproject-pure.toml¶
Parallel pyproject mirroring docs/antikythera-maths/ephemerides-spectral/python/pyproject-pure.toml:
- Uses
hatchlingbackend instead ofscikit-build-core. - Same
[project]block (name, version, deps, classifiers, urls) so the pure wheel and the platform wheel are interchangeable at install time. - Excludes
srmech/_native/*from both wheel + sdist so accidental rebuild artifacts can't leak in. - Version-locked to
pyproject.toml's version by a workflow guard (see "Verify pyproject-pure.toml version matches main" step).
Changed — pyproject.toml: hatchling → scikit-build-core¶
build-system.requires = ["scikit-build-core>=0.10", "cmake>=3.23"]build-system.build-backend = "scikit_build_core.build"- New
[tool.scikit-build]block: cmake.source-dir = ".."points atdocs/srmech/CMakeLists.txtwheel.packages = ["srmech"]wheel.py-api = "py3"+wheel.platlib = false— Phase B2 only, keeps the wheel py3-none-any while CMake validates the infrastructure. Removed at Phase B3.sdist.includeadds the C tree one directory up (the same pattern ephemerides-spectral uses for its CMakeLists.txt + c/).[project.optional-dependencies].devgainsscikit-build-core>=0.10andcmake>=3.23; retainshatchlingfor the pyproject-pure swap build path.
Changed — .github/workflows/srmech-publish.yml¶
Replaced the single-build job with a three-job pattern mirroring
ephemerides-spectral-publish.yml:
build-wheel— scikit-build-core wheel viapython -m build --wheel(the--wheelflag skips the sdist→wheel detour that trips scikit-build-core'scmake.source-dir=".."indirection when the sdist is unpacked).build-sdist—python -m build --sdist, twine-strict-check.build-pure-wheel— swaps inpyproject-pure.tomloverpyproject.toml(saved as.platform), runs hatchling build, restores. Includes the version-match guard + PyPI 512-char description guard, copied wholesale from ephemerides-spectral's workflow.publish—needs: [build-wheel, build-sdist, build-pure-wheel]. Same rc-routing logic;cp -ndedupe in the artefact-collection step handles the case where build-wheel and build-pure-wheel produce identically-named wheels at Phase B2 (will not happen at Phase B3+ when build-wheel becomes platform-tagged).
Phase B7 follow-up¶
build-wheel at Phase B7 graduates from a single Ubuntu cell to a
cibuildwheel matrix (Linux / macOS / Windows × py3.10–3.14). The
trigger for that promotion: C/Python parity tests passing in CI
across all three platforms (Phase B5 complete).
[0.1.1rc3] - 2026-05-13¶
Infrastructure — Task #201 Phase B1: srmech C scaffolding¶
First phase of the srmech build-out to peer-quality with ephemerides-spectral (Task #201). Ships the C tree scaffolding so Phase B2 can wire scikit-build-core in next. Pure-Python wheel contents are byte-identical to rc2 — this release adds files outside the wheel, no API changes, no behaviour changes.
Added — C tree scaffolding (docs/srmech/c/ + docs/srmech/CMakeLists.txt)¶
Mirrors docs/antikythera-maths/ephemerides-spectral/c/ layout:
c/include/srmech.h— public C API header. Status enum (srmech_status_t), version macros, and forward declarations for the three planned symbols (srmech_sha256_hex,srmech_ndjson_iter,srmech_toml_canonical_hash). No definitions yet — those land in Phases B3–B5.c/src/.gitkeep— empty source directory placeholder.c/test/.gitkeep— empty test directory placeholder.c/Makefile— local build/test/parity flow mirroring ephemerides-spectral's Makefile. Phase B1 targets noop gracefully (no .c files → no .a archive); Phase B3 onward they do real work.c/README.md— phase plan, layout, build instructions.c/JPL_AUDIT.md— JPL Power-of-Ten audit log placeholder (populated in Phase B6).c/.gitignore—build/.c/.pages— mkdocs nav stub.CMakeLists.txt(atdocs/srmech/) — top-level CMake driver, mirrorsdocs/antikythera-maths/ephemerides-spectral/CMakeLists.txt. At Phase B1 it short-circuits library creation whenc/src/*.cis empty; Phase B2 wires it into pyproject.toml via scikit-build-core'scmake.source-dir = "..".
Why Phase B1 stops here¶
The scaffolding is intentionally inert at rc3: no .c files means no library is built, the existing hatchling pyproject.toml backend is unchanged, and the wheel content is byte-identical to rc2. This verifies the scaffolding doesn't disturb the existing build before Phase B2 starts moving the build backend.
Phase plan (Task #201 B1–B7)¶
| Phase | Deliverable | Version |
|---|---|---|
| B1 | C tree scaffolding (this release) | 0.1.1rc3 |
| B2 | scikit-build-core + CMake + pyproject-pure | 0.1.1rc4 |
| B3 | srmech_sha256_hex — first symbol + parity test |
0.1.1rc5 |
| B4 | srmech_ndjson_iter — streaming NDJSON reader |
0.1.1rc6 |
| B5 | srmech_toml_canonical_hash — descriptor hash |
0.1.1rc7 |
| B6 | JPL Power-of-Ten audit + JPL_AUDIT.md | 0.1.1rc8 |
| B7 | cibuildwheel matrix + production v0.2.0 cut | 0.2.0 |
Each rc auto-routes to TestPyPI via srmech-publish.yml's rc-suffix
gate; the non-rc 0.2.0 tag is the human-in-loop gate for
production PyPI.
[0.1.1rc2] - 2026-05-13¶
Fixed — hallucination in shipped metadata¶
pyproject.tomldescription,README.md,srmech/__init__.pydocstring: corrected the package's expanded name from the hallucinated "spectral-resonance mechanism" to the correct Stored-Relationship Mechanism (per the srmech research notebook title# Stored-Relationship Mechanism (srmech) — Research Notebookand the project memoryproject_stored_relationship_mechanism_spike.md). The error was caught in the TestPyPI verification of v0.1.1rc1 — the wrong text shipped to TestPyPI as srmech-0.1.1rc1's PyPI Summary metadata; rc2 corrects it.pyproject.tomlkeywords:"spectral-resonance"→"stored-relationship".README.mdStatus line updated to reflect current state (v0.1.0 on PyPI, v0.1.1rcN iterating on TestPyPI toward Task #201 peer-quality cut).
No behaviour or API changes. Wheel + sdist content identical to rc1 except for metadata fields.
[0.1.1rc1] - 2026-05-13¶
Infrastructure — Task #200 Phase A: revert cibuildwheel + add rc-routing¶
This release reverts the premature cibuildwheel adoption from PR #383 and introduces rc-suffix auto-routing in the publish workflow.
Reverted (the cibuildwheel mis-application)¶
.github/workflows/srmech-publish.ymlrestored to the single-build-job shape (python -m buildproduces sdist + py3-none-any wheel). cibuildwheel v3.x rejects pure-Python builds by design ("Build failed because a pure Python wheel was generated") — the matrix that PR #383 introduced was structurally incompatible with srmech's current pure-Python state. Theephemerides-spectral-publish.ymltemplate adopted there legitimately uses cibuildwheel because that package ships a native C library; srmech does not (yet).docs/srmech/python/pyproject.toml[tool.cibuildwheel]configuration block removed. Replaced with an explanatory comment documenting that cibuildwheel returns once srmech grows the C/Python parity surface (Task #201 Phase B).- The failed
srmech-v0.1.1tag was deleted before any artifact reached TestPyPI or PyPI;v0.1.0remains the current TestPyPI release.
Added — rc-suffix auto-routing (srmech-publish.yml)¶
- Tag
srmech-vX.Y.ZrcN→ publishes to TestPyPI (testpypi environment) automatically. No manual workflow_dispatch needed. - Tag
srmech-vX.Y.Z(no rc suffix) → publishes to PyPI (pypi environment). The act of tagging a non-rc version IS the human-in-loop gate for production releases. workflow_dispatchwithtarget ∈ {testpypi, pypi}retained as a manual override path.- Tag-version regex extended to accept rcN suffix:
r"srmech-v(\d+\.\d+\.\d+(?:rc\d+)?)". The version-match check now also logs the routing decision so the run page makes TestPyPI-vs-PyPI obvious. - Same rc-routing pattern simultaneously added to
ephemerides-spectral-publish.ymlfor sibling consistency.
Version-discipline policy (going forward)¶
- Every srmech release between now and peer-quality with
ephemerides-spectral ships as an rc on TestPyPI:
0.1.1rc1,0.1.1rc2,0.1.2rc1, … - No non-rc tag pushed until srmech has Python/C parity, JPL Power-of-Ten C standard discipline, scikit-build-core build, and cibuildwheel matrix legitimately producing platform wheels.
- Each rc-tagged release is auto-shipped to TestPyPI; the next rc iteration is the response to whatever the prior rc-test surfaced.
Tests + parity¶
- All 59 srmech tests pass post-revert (no test changes).
- ephemerides-spectral tests still pass with this srmech version
(the
srmech>=0.1.0floor in ephemerides-spectral'spyproject.tomlis satisfied by0.1.1rc1; pre-release versions resolve normally as PEP 440 allows).
History link¶
Task #200 Phase 1 cibuildwheel adoption (PR #383, merged) → Phase A revert (this release). The premature cibuildwheel adoption was caught by the publish workflow's own pure-Python-wheel sanity check failing under cibuildwheel v3.x's defensive build-time error.
Notes — Task #197 Phase 4 cleanup (2026-05-13)¶
Phase 4 is the final phase of the AMSC-to-srmech refactor (Task #197). It does not change the srmech package itself; it cleans up the upstream duplicate copies in ephemerides-spectral now that Phase 3's import-swap has settled:
- ephemerides-spectral deletes 12 vendored AMSC framework modules (4 top-level +
8 adapters) from its
_research/mirror and itsdocs/antikythera-maths/research/SSOT. ephemerides-spectral's codegen_INCLUDED_MODULES/_INCLUDED_SUBDIRSare updated to no longer mirror the deleted framework into the wheel. - ephemerides-spectral's wheel shrinks by ~37 KB (~4.7 %) and its codegen
manifest.jsonn_files drops from 154 to 142. - All 5 Phase 1 parity gates remain green at the Phase 4 boundary; srmech in-isolation 59/59 tests pass (unchanged from Phase 3); ephemerides-spectral pytest is byte-identical to the Phase 3 baseline (2128 passed + 42 skipped = 2170 collected).
srmech v0.1.0is now ready for the first TestPyPI release. SeeTESTPYPI_RELEASE_NOTES_v0.1.0.mdin this directory for the release procedure (autonomous TestPyPI publish via thesrmech-v0.1.0tag through.github/workflows/srmech-publish.yml; PyPI release remains human-in-loop).
[0.1.0] - 2026-05-13¶
Added¶
- Initial extract of the AMSC framework from
ephemerides-spectralas part of Task #197 (AMSC-to-srmech refactor, Phase 2). The framework lives undersrmech.amsc.*: srmech.amsc.format— Mathematical Provenance Record (MPR) v1 format:MPRRecorddataclass, NDJSON streaming IO (read_ndjson/write_ndjson),validate_mpr_record,sha256_bytes, schema-version + mandatory-field constants.srmech.amsc.descriptor— descriptor TOML loader:Descriptor,load_descriptor,discover_descriptors,render_template(deliberately minimal name-substitution + Python format-spec; no Jinja),descriptor_hash(canonical-serialised),DescriptorValidationError.srmech.amsc.catalog— universal bridge surface:list_attested_sources(withadapter_classfilter),get_attested_dataset(paginated, T0+T1+T2+T3 tiered),get_attested_descriptor,attestation_audit,iter_attested_dataset, T2 local-kernel overlay (use_local_kernel/clear_local_kernel/get_local_kernel_state).srmech.amsc.gap_suggester— schema-gap-driven trigger (suggest_gap_collections); the lazy-imported classifier + probe sources are ephemerides-specific and remain in ephemerides-spectral.srmech.amsc.adapters— six adapter modules:html_scraper,json_api,csv_bulk,netcdf_grid(stub),geotiff_bbox(stub),literature_curated; plus_base.py(ADAPTERSregistry,attest,parser_rule_hash,runcomposer).register_attested_root(path, *, source)— the load-bearing cross-package API added insrmech.amsc.catalog. Downstream packages whose catalog SSOTs live outsidesrmech/amsc/attested/push their roots at package-import time; subsequent_descriptors()calls enumerate the union of srmech's own root + all registered roots in registration order. Conflict policy: first-registered wins with a warning.list_registered_roots()— introspection of currently-registered roots (srmech's own + every external). Used by tests and diagnostic output.srmech/amsc/attested/— empty SSOT subtree reserved for future srmech-primary catalogs (e.g. thecitations_curatedcatalog planned for Spike #23).- CI workflows under
.github/workflows/: srmech-ci.yml— pytest on push/PR againstdocs/srmech/python/**, 4-cell matrix (Ubuntu/macOS/Windows × Py3.12 + Ubuntu × Py3.10 floor).srmech-publish.yml— build sdist + py3-none-any wheel onsrmech-v*tag, publish to PyPI via trusted OIDC; manual workflow_dispatch can target TestPyPI.srmech-autotag.yml— autotag onpyproject.tomlversion bump.
Notes¶
- Phase 2 is purely additive. No ephemerides-spectral files are touched. Phase 3 (separate PR, not yet open) will rewire ephemerides-spectral's bridge to import from
srmech.amsc.*; the byte-identical-wheel parity gate from the Phase 1 scope document applies there, not here. - Cross-package gap_suggester deviation.
srmech.amsc.gap_suggester.suggest_gap_collections()lazy-imports.dynamical_regime_catalogand.dynamical_regime_probes_data, which are ephemerides-specific and not shipped by srmech. Calling the function from a context where those modules aren't reachable (e.g. srmech in isolation, no ephemerides installed) will raiseImportErrorat call time. The Phase 1 scope did not flag this; ephemerides-spectral consumers (the only known caller) are unaffected because the relative imports resolve inside ephemerides's_research/mirror until Phase 3, then via Phase 3's import-swap. parser_versionstamp. Changed from"ephemerides-spectral X.Y.Z"to"srmech X.Y.Z"in T3 live-fetch attestation blocks: srmech is now the parser. Committed NDJSON files retain whateverparser_versionwas stamped at collection time; only future T3 runs differ. No effect on the Phase 3 wheel parity gate (T3 is runtime, not committed bytes).