All ten scenarios pass under asserts and all four required under release, on
Yggdrasil, in the committed debian:13 image. Worst overshoot anywhere was 4 ms
against a 400 ms tolerance. F1 is closed on Linux: the build that ships now
runs the timeout rule.
Running the README's own worked example is what found the second thing. It
cannot work: the test binary carries an $ORIGIN rpath and nothing puts
libmsquic beside it for a test build. That example was written in the same
commit that deleted the harness, so the working path was removed and the
broken one published together.
Two things stay open. Windows still owes its release configuration for the
same reason Linux did. And F5's stress wants sixteen CPU burners on the
platform where burners are now forbidden, which is the operator's call rather
than mine to route around.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The pass ran against 52082ec. Its nine hand probes, including the Rust
both-hops probe that proved S-3 on a matched pair, sit unpushed on soul-probe
in a scratch clone. Probes are throwaway by doctrine, so the pointer says both
where they are and that the findings recorded in this map are the durable
copy. Fix batch 4's tests have to reproduce the behaviour independently
anyway.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
My previous commit rewrote the batch-8 rulings as if the batch were still
owed. It was not: it landed at 5646ff7 and the harness went at 796433f, both
on hands/cultmath-erf. The map was behind the code, not the other way round.
What that changes: the release configuration and the seven mutation entries
landed inside the harness and left with it two commits later, so the release
build is owed again as a plain build. The scenario runner and every seam
survived untouched and now run as direct arguments to the binary.
Cut 3's only outstanding work is the Linux leg.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Four of the six rulings for the eighth fix batch named harness entries. The
operator retired the hand-written mutation harness the same day, with no
fallback, so those clauses are dead text. What each ruling protected is not:
the release configuration, the seam wrapping the whole duration expression,
pollhammer against a runtime holding live objects, the zero and negative
timeout bounds, and closerace surviving starvation all stand as behaviour.
The bridge is C++ and no ecosystem mutation tool is wired here, so it gets no
committed mutation evidence at all. Its rules are pinned by scenarios at the
layer that decides them; a mutant is something Soul tries by hand and throws
away.
F5's 16 burners now run on Yggdrasil. Those burners are what froze the
operator's workstation.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The triage landed on the fifth attempt. Most of the 85 survivors are message
strings; about a dozen are real, and they sit on the rules the cut exists to
enforce: the alias-numeric refusal degrades to "only if every schema agrees",
a half-blank cites.target stops being refused, the index-alias role naming is
untested at all four sites, the row tiebreak and EdgesFor's ordering are
pinned by nothing, and ComputeDigest's hop branches have no coverage at all.
The equivalent mutants are named so nobody chases them twice.
R-AA's triage half is done; what it now owes is a behavioural test per live
survivor.
Soul also withdrew its own claim that C#'s cursor digest does not sort its
lists. It does, at CultNetSelectionEvaluator.cs:637.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Cut 1 may not merge. Row identity becomes (schemaId, recordKey), the hop
behaves identically in both runtimes, the error message obeys its own schema,
and cultnet-rs becomes mutable by cargo-mutants.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Refusals become wire messages, cursors are keyed, one id reaches the
authorizer, and v0 lowering preserves v0's pre-cut answers exactly,
correcting the earlier ruling that lowered an empty list to no filter.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The C# reference never serves v1, cited returns the wrong edges in both
runtimes, and the runtimes disagree on floats, schema aliases and ordering.
Floats render as their exact decimal expansion, flagged for the operator.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Seven passes on one rule, each killing the last Soul's functions and each
followed by another that is identity at every probe. Wall-clock observation
from outside cannot converge on an arithmetic rule. A dev-only seam records
the computed wait, and equality kills the whole family.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The reconcile tests lost what they proved, the shard snapshot still loops on
its own, and the public options type silently drops every term v0 cannot
carry. Empty lists are refused at the one door, Mesh refuses what it cannot
send, and the dead identity code is deleted.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The operator ruled decimal strings on 2026-09-17 and the map recorded it in
section 16, but section 2 and D8 still said float64 and nothing specified the
canonical form or the comparison. Commit 1 built what section 2 said. The Rust
Hands stopped on the conflict before writing a line.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
1.0.60 removed public API (CultInspectorAssetPath) under a patch bump; its
own changelog says "Breaking" and the version number lied. Operator ruling:
semver is policy, strictly, for every published package family.
docs/semver-policy.md states what counts as breaking per surface (managed
API, Unity inspector attributes, CultCache wire format/store compatibility,
CultNet protocol, native bridge exports), the 0.y.z pre-1.0 convention, tag
naming, and required changelog shape. Records the 1.0.60 scar as history;
does not renumber it.
scripts/check-changelog-semver.mjs is the mechanical check: refuses a
changelog "### Breaking" section under too small a bump, and refuses a
version that skips, reverses, or repeats the previous published tag. Skips
re-litigating a version that is already tagged (rebuilding immutable
history is not a release action). Wired into publish-packages.yml for the
four npm/PyPI packages, and into build-unity-package.ps1 (cultlib),
packages/cultmath/scripts/build-unity-package.ps1 (cultmath), and a new
scripts/verify-caching-unity-release.ps1 (caching.unity has no build script
of its own to hook into).
18 node:test cases cover the required table (breaking+major/minor/patch,
non-breaking, 0.y.z, skip, downgrade) plus CLI-level git-tag resolution.
Hand-mutated each rule (bump classification, pre-1.0 lane requirement,
breaking-heading detection, breaking early-return, already-published skip)
and confirmed each kill, then restored byte-exact. No mutation harness is
committed; CultLib has none today.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
No constant sits in both bands, which is true and is not the rule. A wait that
caps itself at a second passes every scenario, and capping a wait so shutdown
gets noticed is the most ordinary way that line will ever be written. An offset
and a scale survive beside it.
A fresh Windows clone also cannot run the documented Linux path at all, because
nothing tells git to leave shell scripts alone and the container dies on a
carriage return in the interpreter line. The mount fix works; this sat behind
it.
The equivalence holds, but on two premises nobody had written down, and
narrowing both wakes survives while being equivalent to nothing.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The blind spot was not where the hold sat. No scenario ever let a timeout
finish a poll, because both ended their waits with a close, so the duration
governed nothing anyone could see. A scenario that polls an idle runtime and
measures elapsed against what it asked kills all three constants, and it asks
twice at values whose bands do not overlap, because one value cannot tell a
bridge honouring the host from one waiting on that same number of its own.
The probe that reached the two-phase poll through a closed port is committed,
the wake finally has the entry the table had been claiming, and a surviving
mutant is argued equivalent and handed to Soul rather than swapped for an
easier one.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
A reference whose target contradicts its declaration is noticed on read in this
cut and refused by name. Writing the same check into the put path is intended
rather than optional: the declaration already names the target and the writer
already holds the record, so it is cheap, and leaving it to consumers means
every tool that writes records reimplements it and a tool that forgets looks
exactly like one that did not until something refuses.
The consumer story rests on less than it sounds. There is no Studio repository
and no project by that name; the tooling that enforces this today is one
console project in one game. That makes now the cheapest moment to move the
rule to its owner.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
A generator runs against published schemas, and the published schema entry is
flat: the cache walks the base types to collect members and then throws the
hierarchy away. Fed that, a generator emits one flat class per leaf with the
inherited members copied into each, which is the opposite of building the
hierarchy up for you.
So the catalog carrying each leaf's base chain is a prerequisite of that
campaign rather than a nicety for naming families, and families fall out of it
once it exists. The campaign itself waits until this cut settles the shape it
would be generating against. No generator exists in the repository today.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
A caller needing exact pairs asks once per schema. The limit is stated rather
than answered with a third addressing list.
Naming a family is parked, but not because there is nothing to name. In one
process a family is an assignability check; over the wire there is no type
system, and nothing publishes the hierarchy where a caller can see it. The fix
is a catalog entry naming each leaf's base chain, which is small work that
moves the catalog's shape, so it belongs in a cut already touching it.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Floats are fragile, and every value in this vocabulary is already a string, so
decimal strings keep the wire to one value encoding rather than adding a
second. The recommendation this reverses had that backwards.
What it costs is a comparison rule, and that is where the danger sits: compared
as text, ten is less than nine. The cut owes a canonical decimal form and a
numeric comparison, both pinned, and it no longer owes anyone a sentence about
where exactness runs out.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
A stranger can now rerun both targets from the committed container and the
README, which was the batch's point and holds. The rules are another matter.
The scenario holds its callers after the wait whose duration is the rule, so
a hard-coded two seconds walks through the whole matrix while the committed
entry only ever died because one constant was smaller than another.
The gap recorded as unreachable is reachable in fifteen lines against a closed
port, and the mutant crashes a plain optimised build on contact. Second gap
falsified today. The next batch fixes the cause once instead of adding another
entry at another constant.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The operator ruled ordinals, the two deletions and the leaf titles earlier
today, in the other campaign's map. This one still carried them as open, and
a stale live question is exactly what a later agent reads as a decision still
to make.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The cache already declares and persists what type each member is, so numbers
cost no migration. What they do cost is a public way to read a declared value
at all, because the evaluator today cannot reach one from outside the cache,
which is why the existing snapshot loop reflects. That is a hundred and fifty
lines, not sixty, and the ledger moves the wrong way to say so.
Three claims inherited from the earlier pass were wrong and were cut rather
than designed around. A fifth selector engine turned up in Rust. And the
cache's index maps keep one key per value, so an evaluator that reached for
them would answer a query about a whole family with a single row.
Watching a selection is mapped as its own cut, where nine overloads that are
a query language in disguise collapse into one.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The map carried a one-line invocation that installed no JavaScript runtime,
so it could not run the mutation runner, and said nothing about disabling
randomisation or relaxing seccomp. That is how the Linux path came to be red
for everyone except the agent that had found the flags by hand. The recipe now
lives with the image and the bridge README, and the map points at them.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Three selector engines evaluate the same two allowlists today, two of them
carrying identical copies of the same reflection, and projection is a delegate
no production code ever constructs. The map replaces all of it with one
selection a caller can state: any-of over the aliases the cache already
declares, a projection it asks for, an order, a cursor, and one hop along a
declared reference.
The audit is reported as measured rather than as hoped. About 326 of 21,720
lines collapse in CultMesh, and the repository grows by about 370, because the
Rust runtime gains a capability it never had.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The lifetime fix holds under a thread sanitizer and the assertion kills the
deleted wait on both targets. The harness defending all of it does not hold.
Every native kill this cut had reported came from scratchpads that no longer
exist, the committed Linux path dies before its scenario starts and hides the
reason, and the scenario stays green when calls are not counted at all or
when nothing ever parks.
Two rules are defended by nothing committed. They are recorded as undefended
rather than given a number that would look better.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The cut does not close. The lifetime model was right in shape and wrong by
one line: the last call notifies after it has released the lock, so the
runtime can be destroyed in between and the wake lands on freed memory. A
sanitizer found it on the unmutated bridge.
The mutation that survived by construction is ruled killable rather than
accepted. Under a sanitizer with pollers blocked mid-copy it fails four times
in forty-eight, so a dev-only assertion makes it fail every time instead. Two
further mutations are honest only on the sanitizer target and the table must
say so, because a mutation whose kill depends on the target is not killed
until it runs there.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The bridge keeps an object alive across the call that names it, closes
each handle exactly once, and quiesces before it frees. The contract is a
header hosts and tests are checked against. Return codes are negative on
every platform, one completion per frame, and every connection's end is
observable. One mutation survived by construction and is handed to Soul
to rule on.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The bridge's lifetimes are wrong: a lookup hands the host a pointer the
worker can free before the next call. Release crashes on both platforms
under an ordinary stream teardown. Three ABI gaps ride with it, and the
return-code contract inverts between Windows and POSIX. One digest in
this map was the zip's, not the DLL's. All in a fix batch before Cut 4.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
One credential type on both platforms, the provider role works, the ABI
is unchanged. The tree pays four megabytes more and the Unity plugin's
copy diverges until its rebuild. The map's claim that PKCS12 was one type
for both providers is withdrawn.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The bridge is a runtime-neutral two-role event queue, built and smoked
on both platforms with the Linux artifact named as development-loop only.
Two hangs the old shape never exercised are pinned. One fork stops the
Windows provider: the pinned Schannel flavour does not implement the
PKCS12 credential the spec assumed was one type everywhere, while the
same bridge passes beside the OpenSSL flavour. Recommended: pin OpenSSL
on Windows and pay the tree size.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Byte parity held both ways under Soul's own frames. Three small things
did not: a mutant declared equivalent was not, the payload copy is a view
for a Node buffer, and the frame parity has no CI executor. The breakage
of the aggregate scripts dates from the scoped rename, not the version
bump this map first named.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The frame codec exists with parity proven both ways from its first
commit, and its test found a real divergence before any consumer could:
the JavaScript decoder deletes a leading byte-order mark the .NET decoder
keeps. Two housekeeping rulings taken: the aggregate TypeScript scripts
have been broken by a rename for two days and get their one-word fix, and
the mutation runner is renamed for what it now covers.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>