Commit graph

2,368 commits

Author SHA1 Message Date
Meta Vens
b82c76dfd6 Add the Cut 6d mutation suite
Five entries, each restoring the permissiveness the shape it pins
removed: M23 and M24 the one-per-subject and one-per-repo keys, M25 and
M26 the sequence anywhere but last, M27 a nested subject stripped of its
own sequence on the way into the key. Each dies on the test named beside
it, M0 green.

The file records what has no entry and why: the `n` marker is a fixture
pin, the `u32` type has no runtime mutation, and date-invariance is
covered by M24's test asserting a stewardship local is exactly two parts.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-16 18:37:12 +02:00
Meta Vens
9699751f00 Key a resolution and a stewardship by sequence
A subject's resolutions and a repo's assignments on a mind each carried
one key, so the second record of either overwrote the first and a
withdrawn resolution could not be kept under its subject. Both keys now
end in a writer-set `sequence: u32`, `n<N>`, last so the history of a
subject or a repo is one key prefix and R2 keeps that prefix exact:
`question.Q1.n` cannot match `question.Q10.n1`, and a withdrawal keys one
nesting out under the resolution it withdraws.

`assigned_on` stops being a key input and stays a field, validated where
every field is, so the same assignment recorded on two days is one
document and a repo transferred away and back on one day does not
collide. A hand-off is unchanged in shape and key: a transfer back is the
same document with the instances swapped, and no field names a return.

Nesting arithmetic moves with the shape: each nesting prepends
`resolution.` and appends `.n<s>`, so a chain composes `14 * (d - 1) + L`
bytes against `LOCAL_MAX`, and both fixtures key four deep. The leaf
refuses no sequence value -- not `0`, not a gap -- as it refuses no
`revision` value; which sequence a document may claim and which record is
in force are admission's, and are not in this crate. `pipeline_id` gains
no arm and no key is parsed back.

Two schema files regenerate from the derivation, gaining the property,
the required entry and the new doc comments. No epoch moves: no store is
written at this epoch and no reader is pinned to the file.

Cut 6b's M2 is re-anchored on the new parts line, as N7 and N8 re-anchored
Cut 6's M12 and M13.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-16 18:26:46 +02:00
Meta Vens
ceeffabf26 Close Cut 8 as ruled today
Every promise of the fix batch held under non-revert mutations. What
remains belongs to the key-sequence follow-up: a withdrawal of a
withdrawal is not yet refused, and reopening a subject needs admission's
in-force derivation to ignore withdrawals as well as the key to allow a
second number. Six test gaps ride with it.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-16 18:25:50 +02:00
Meta Vens
b693f9f3b3 Map Cut 6d: resolution history and stewardship by sequence
Two key shapes gain a writer-set sequence so a subject's resolutions and
a repo's stewardships keep their history under one prefix, with the
in-force record derived as the latest not withdrawn. A hand-off stays a
transfer with no lease field. Two schema files regenerate, no epoch moves,
and the Huginn follow-up commit is named for when the pin moves.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-16 18:19:25 +02:00
Meta Vens
542dc1848c Rule Q19 and Q20: no double withdrawal, and stewardship by sequence
A withdrawal is final and a reopened subject resolves again at the next
number. Stewardship keys by a per-repo sequence rather than a date, so
two transfers of one repo in one day do not collide and both histories
use one mechanism. Q18's substance stands; its wording is superseded.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-16 18:12:16 +02:00
Meta Vens
e306964d04 Record the Cut 8 fix batch
The epoch gate checks what the spec said it checks, the organ mints no
leaf refusal, one swap and every strong read are pinned, and every rule
row the suite could not see regress now has a mutation. Self's Q17
default was reverted before it mattered. The gap the ruling leaves until
the leaf's key sequence lands is named as the reason Cut 6d precedes Cut 9.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-16 18:08:48 +02:00
Meta Vens
ec0ff804bc Make Q17 carry its own conditions
The operator questioned the model behind keeping withdrawn resolutions.
The answer is that the ruling matches the campaign's standing rule of
never overwriting, and that the record only earns its keep if Cut 9 shows
a subject's history and Cut 11 indexes withdrawals with their reasons.
Both are now requirements on those cuts.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-16 18:04:58 +02:00
Meta Vens
76167f0a2a Correct Q18: a hand-off is a transfer, not a lease
Self read the operator's lease example as the general case. It is one
case: a steward can also spin off a new steward permanently. A hand-off
is a transfer of stewardship with no lease field; a lease is two of them.
The date-keyed stewardship stands because it is what lets a repo come
back at all.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-16 17:59:53 +02:00
Meta Vens
993600fd6b Rule Q17 and Q18 in the operator's words
A resolution is the record of how a subject was closed; withdrawn ones are
kept under the subject and a subject can be resolved again. A hand-off is
a lease: a campaign's steward borrows another steward's authority over a
repo and returns it. Both are key changes in the leaf, free today, mapped
together as one cut. Self's default on Q17 was withdrawn before it landed.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-16 17:56:34 +02:00
Meta Vens
87d3420c31 Record Soul's Cut 8 pass, and raise Q17 and Q18
The owners hold at the layer where they fail: a mind copied under another
instance is refused, a second process is refused, and the digest moves
with content alone. What did not hold was the epoch gate, which took the
first record and checked nothing about it, and fifteen rule rows the suite
could not see regress. One sentence of the spec contradicted the key
grammar and becomes a question: whether a resolution can be undone.
Another surfaces for Cut 12: whether a stewardship can come back.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-16 17:45:42 +02:00
Meta Vens
3bd0826d48 Rule out a hidden dependency on the leaf
The workspace's inverse dependency tree lists the leaf alone in every
dependency kind, so the recompile of epiphany-core after a leaf-only
change was a shared target directory evicting a fingerprint under another
session's build, the accepted cost of sharing one directory. Not a
dependency, and not the explanation Hands gave.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-16 17:28:03 +02:00
Meta Vens
32b638dcec Record the leaf's serialisation derives, and a recompile that should not have happened
The document and refusal types now serialise with a tag equal to the kind
name, which is what lets Cut 10 carry a batch without a second registry.
An unexplained recompile of epiphany-core after a leaf-only change is
recorded as suspect for the next Soul, not accepted on the explanation
offered, because the core is not a dependent of the leaf.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-16 17:27:31 +02:00
Meta Vens
b4b17fc895 Serialise the pipeline envelope and its refusals
The leaf owns the document and refusal types, so it owns their
serialisation. Huginn's admission batch cannot derive Serialize while
PipelineDocument does not, and mirrors PipelineRefusal through a
serde(remote) definition kept in step by hand; neither is the wire's
business.

PipelineDocument is adjacently tagged on the kind name: a document on a
wire is { kind, value }, and kind is exactly PipelineKind::name(), so a
reader dispatches on the kind segment it already knows from the key and
needs no second registry mapping variant spellings to kinds. Adjacent
rather than internal because a value is a named map of its own.
PipelineRefusal stays externally tagged, serde's default, which is what
keeps every variant's field and value on the wire under their own names.

No kind, field, type id or epoch moves, and the published per-kind
schemas are untouched: the envelope is the wire's shape, not a document's.

every_document_and_refusal_serialises_and_reads_back round-trips every
sample document and every refusal variant through serde_json and
rmp_serde, and holds each derived schema's variant list to the thirteen
kinds by name and to the four refusals. Mutations D1 (the tag rename
removed) and D2 (a refusal's field skipped) are both killed by it.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-16 17:26:30 +02:00
Meta Vens
dfbc5700bc Record the Huginn half of Cut 8
The organ opens a mind fail-closed over CultCache's owned store, lands a
batch whole with a receipt naming its bytes, and admits through one
ordered path with every cross-document rule in it. Twenty mutations,
twenty killed, inside the build budget. One finding for Cut 10: the leaf
must derive serialisation on the document and refusal types before a
batch can cross a wire.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-16 17:21:09 +02:00
Meta Vens
78238ebabc Record the harness repair batch, and say whose lock it is
The harness now handles a sidecar that outlives its file and a nested
sidecar without a raw exception, and the leaf's doc comments describe the
leaf. Cut 8's store paragraph now says the single-writer mechanism is
CultCache's owned store and its compare-and-swap batch, with redb as the
engine beneath, after the operator caught the misattribution.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-16 16:55:19 +02:00
Meta Vens
aef0e1bf5a Describe the pipeline leaf by its own contract, not by Huginn
Doc comments only. The crate doc, `prepare`, `register_pipeline_document_types`,
`ForeignDocument`, `PIPELINE_SCHEMA_EPOCH` and `validate_pipeline_write_envelope`
spoke of "the memory organ" and "Huginn" in the present tense as a consumer
that exists; Huginn's crates are empty stubs at this commit. Each now says what
the item does and guarantees, and where a consumer must be named, calls it the
organ that will admit these documents. The sentence claiming Huginn pins the
foreign-document rule against its real receipt type is gone; no such test
exists.

`decode` now says what it does not do: it validates neither bounds nor the
key. A decoded document is a typed read, and `validate_pipeline_write_envelope`
is the check before a write.

No code change: `cargo test -p epiphany-pipeline --lib` is 27 passed with no
warnings, and `schemas/cultnet/` and `Cargo.lock` are untouched.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-16 16:54:16 +02:00
Meta Vens
b3bd4a82e4 Repair a missing file from its sidecar and stop on a nested one
Soul's XF: a sidecar whose file was gone hit the `if`/`else` empty-array
collapse of Windows PowerShell 5.1, so `Find-FirstDifference` threw a raw
`ArgumentNullException`, the sidecar stayed, and every later run in that
repo died the same way before M0. A missing file is now its own case,
decided before any comparison: it is recreated from the sidecar, whose
bytes are the only copy, hash-verified, and the run says so.
`Find-FirstDifference` refuses a null array with the harness's own words.

Soul's XG: a sidecar of a sidecar was repaired in enumeration order, so
the inner sidecar's body became the file and the outer one then crashed
on its missing target. No run leaves that shape and the two cannot be
ordered, so the repair pass now stops before touching anything, naming
both files.

Soul's F2: the `.eureka-mutation-overwritten` copy was written before the
restore was attempted, so a locked target (XC) left a copy claiming an
overwrite that never happened. The copy is still written first, because
once the restore lands it is the only home of the pre-repair bytes; a
restore that fails without opening the file removes it again, and one
that changed the file before failing keeps it and says so.

Every repair step now sits inside one `try`/`catch` per sidecar, so the
"could not repair" message is the error that escapes, with the cause
appended, never a bare .NET exception.

Reproduced against a scratch copy with Soul's soul-cut8e-harness-attack:
XF recreates the file and removes the sidecar; XG stops with both files
named and the target untouched; XC leaves no overwritten copy. XA, XB,
XD, XE, XH, XI unchanged. An empty existing file beside a sidecar (XK)
repairs without the null path.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-16 16:51:14 +02:00
Meta Vens
0b8d60a685 Record Soul's pass on the Epiphany half of Cut 8
The door holds from outside: the four functions and the constant are
reachable, the wrappers and the id reader are not, and every kind
round-trips through the public path with its own key. Huginn pins
a65c6420. What Soul found is a harness that crashes without a word when a
sidecar outlives its file, and doc comments that describe an organ nobody
has built yet.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-16 16:46:25 +02:00
Meta Vens
5a71c4bb3e Rule Q13 and Q15: types from huginn-mind, and mind.redb
The client depends on the organ's library for types only, pinned by a grep
that it never opens or admits. The mind file is named for the engine that
holds it, because the selector and Studio both key on the extension. The
engine's provenance is recorded beside the ruling so nobody asks again.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-16 16:39:16 +02:00
Meta Vens
955b7672c8 Record the Cut 6c fixtures and the Epiphany half of Cut 8
The fix outcome's sha is checked whether or not a reference is given, the
supersession bound is pinned in the validator and not only the schema, and
the harness repairs any sidecar under the repo and never discards bytes
without saving them. The leaf's write path is live and public for the
organ, with the epoch back in code as one constant.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-16 16:23:21 +02:00
Meta Vens
a65c6420f6 Cut 8, Epiphany half: the leaf's write path goes live for the organ
`epiphany-pipeline` was the organ's type library with its prepare,
decode, register and validate path gated `cfg(test)` "until the organ
prepares writes (Cut 8)". This is Cut 8. The four are `pub` and
un-gated: `register_pipeline_document_types` registers the thirteen
kinds and nothing else, `PipelineDocument::prepare` and `::decode` are
the one door to the crate-private wrappers, and
`validate_pipeline_write_envelope` returns `PipelineRefusal` directly,
which drops the `downcast_ref` in the key test. `anyhow` and `rmp-serde`
move from dev to normal dependencies; both were already in the lock, so
the lock is unchanged. The test stand-in `ForeignDocument` stays
`cfg(test)` and is registered by the tests' `schema_cache`, no longer by
the live registrar.

`PIPELINE_SCHEMA_EPOCH` returns as one `pub const` beside the schemas it
names, with the README's evolution rule as its doc comment: additive
keeps it, a widened `PipelineKind` is additive, a breaking bump refuses
the old store. `every_kind_is_at_the_epochs_version` reads the version
off the constant and holds every type id to it, and counts the live
registrar's types against `PipelineKind::ALL`.

Verification: `cargo check -p epiphany-pipeline --lib --tests` and
`cargo test -p epiphany-pipeline --lib` (27 passed, 0 warnings);
`cargo check -p epiphany-core --lib --tests` untouched; `cfg(test)` in
lib.rs is now only `derived_schema`, `ForeignDocument` and `mod tests`;
`schemas/cultnet/` and `Cargo.lock` unchanged. Mutations
`tools/eureka-cut8-epiphany-mutations.psd1`: M0 green, E1 (epoch `.v2`),
E2 (one kind fewer registered), E3 (the stand-in registered again under
`cfg(test)`) all killed by the new test.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-16 16:21:34 +02:00
Meta Vens
3f7d58d191 Harness: repair every sidecar in the repo, and never overwrite silently
Soul's extra attacks on the mutation harness found the crash repair too
narrow and too eager. A sidecar left beside a file outside this run's
targets was ignored (XA), so a dead run's mutant survived in the tree; a
hand edit made after a crash was overwritten by the sidecar without a
word (XB); a locked target threw a bare IOException with no path (XC);
and a sidecar equal to its file was reported as a repair (XD).

At startup the harness now walks the repo root (`target/`,
`node_modules/` and `.git/` pruned) for every `*.eureka-mutation-original`
and repairs each. A file equal to its sidecar is left alone and the run
says "sidecar matched; nothing to restore". A file that differs has its
current bytes written to `<file>.eureka-mutation-overwritten` first, with
the path and SHA-256 printed. A write that fails prints the path and
"could not repair, sidecar kept" before rethrowing.

`-Repo` names the root the entries, targets and commands are relative to,
defaulting to this script's parent, so a suite in another repo runs
through this one harness.

Reproduced against a scratch copy with a fake runner: XA repairs b.rs
and removes its sidecar; XB keeps the hand-edited bytes in the
overwritten copy (same hash) and restores the original; XC stops with
the path and the sidecar kept; XD removes the sidecar and restores
nothing.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-16 16:16:11 +02:00
Meta Vens
fcfbda3f94 Pin the three Cut 6c rules Soul's mutations survived
Soul's own suite found three validator rules no test measured: a fix's
commit was unpinned once `by` was `Some`, the validator's own bound on a
supersession was unpinned (only the schema's `maxItems` was), and the
reason on `Recorded` and `Withdrawn` had no bound test. Each is now a
forgery in the test that owns the arm: `Fixed { commit: "dirty-worktree",
by: Some(ruling) }` refused at `resolution.outcome.commit`; nine
supersessors refused as `FieldBound` at `resolution.outcome.by`, eight
accepted; a 1,001-byte reason refused at `resolution.outcome.reason` on
both outcomes.

Soul's S2, S14 and S10 join the Cut 6c suite under their own ids. Rerun:
M0 green (26 passed); M16-M22, S2, S14, S10 killed.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-16 16:14:41 +02:00
Meta Vens
fc7ad7457a Record Soul's pass on Cut 6c
Every shape, schema and key promise held, including byte-identical derived
schemas and unmoved keys. Three fixtures were missing, the important one
being that a fix outcome's commit sha goes unchecked whenever a reference
is also given, which is half of ruling B. Three harness follow-ups. One
count of Self's corrected.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-16 16:09:14 +02:00
Meta Vens
c792075ec8 Refresh Cut 8 against the landed grammar and shapes
The old section was anchored to pins from before every key moved and
before the Ghostlight shapes retyped the referents admission validates.
Two things the old section had wrong: Epiphany is touched, because the
leaf's prepare, decode, register and validate path is test-only today and
Huginn cannot re-declare the wrappers without becoming a second authority
over the wire shape; and the rule set lived only in git history, so the
map now owns it in full. The store is the owned redb store, which gives
one-writer a lock instead of a sentence, and the receipt is duplicated,
bounded and smaller, with moving it priced and declined for now.

Four questions raised; two are defaults taken, two are for the operator.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-16 16:01:39 +02:00
Meta Vens
ca7e230cd8 Record Cut 6c's landing and the third harness batch
The Ghostlight shapes are in: two unit enums, a promise type, five fields,
four parsed referents where strings were, and one estimate type where two
were. Six schemas regenerated with the supersession bound carried into the
published JSON. The harness now restores on every path and repairs itself
after a killed run. Four scars recorded, the first being that a widened
type only reads as a kill when the fixture is spelled so the wider tree
still compiles.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-16 15:44:00 +02:00
Meta Vens
dddf9ede53 Cut 6c mutation suite, M16-M22
Seven entries through the shared harness: the Superseded list checked by
length only (M16), the outcome's whole match body replaced by Ok (M17),
the fix's commit unvalidated (M18), the mutation label widened to Short
(M19), its commit widened to Short (M20), the sample estimate's line
counts transposed under a two-line anchor that carries the opener (M21),
and a claim's mutations bound widened to sixteen (M22). Each is killed by
the Cut 6c test that pins it; the key suite is blind to all seven.

The first run left M19 and M20 with no verdict: the sample and the test
spelled the label and the commit with the narrow constructors, so the
widened tree did not compile instead of admitting the forgery. Both now
go through From<&str>, which every text type has, and the forgeries
reach validation and kill.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-16 15:41:41 +02:00
Meta Vens
13570e8458 Cut 6c: the Ghostlight shapes
Ghostlight's independently run campaign named what the documents could
not say: a report's promises, a ruling's authority, a finding's origin,
and a resolution's referents as anything but unparsed text. Zero new
kinds, no key moves, epoch unchanged.

Added: RulingAuthority and FindingOrigin as unit enums; Promise beside
LandedName, identified by its report and label like a TargetInvariant;
promises on the cut report, authority on the ruling, origin on the
finding, and promise plus a bounded mutations list on a verdict claim.
Retyped: MutationRecord carries a key-safe label, a location, the exact
before and after, and the commit it ran against; the cut spec's estimate
is a StructuralDelta, the same shape as the report's actual, so the two
compare on every axis; ResolutionOutcome's referents are PipelineRefs,
Superseded a list of at most eight (its maxItems carried by schemars
extend, since the enum is outside value_types!), Fixed a Sha with an
optional record (ruling B). The Bounded impl is the replacement written
whole: the list validates every item, and every arm names its field.

Five tests pin the shapes: a referent is a full id of the kind it
declares, with a wrong-kind forgery at by[1] refused by field, and an id
no resolution derives left to admission; a fix's commit is lowercase hex;
a mutation's label is dot-free and its commit a sha; a claim's promise
and mutations are labels, nine mutations refused at eight; the sample
spec estimates a net addition of 900 lines and equals the sample
report's actual.

Six schemas regenerate from the derivation test's temp directory:
ruling, cut_spec, cut_report, verdict, finding, resolution. index.json
and the other seven are byte-identical.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-16 15:33:25 +02:00
Meta Vens
4d6af4090f Cut 6c deletes: the estimate type, the outcome's Bounded impl, the mutation text
The deletes go before any field is added, so the dead estimate shape and
the delta that replaces it never coexist at a commit. SubtractionEstimate
and its comment, the cut spec's subtraction_estimate field and its sample
literal, the hand-written Bounded impl for ResolutionOutcome (replaced
whole in the next commit, never patched, since every arm changes referent
type and a patched impl is how a length-only check survives), and
MutationRecord's free-text mutation field with its sample value are gone.
The list-default comment now stands on StructuralDelta alone.

This commit does not build: PipelineResolution's outcome has no Bounded
impl until the next commit gives it the parsed one.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-16 15:29:19 +02:00
Meta Vens
7cd1a38bc8 Pin the reader against a trimmed trailing dot on the root and the local
Soul's X1 and X11 survived the second fix batch: a reader that trimmed a
trailing dot from the root or from the local before its dotted_text check
passed every test, because the only fixtures with a trailing dot went
through the writer, which never composes one. Four malformed ids now
read through pipeline_id in keys_read_back_as_ids_of_their_kind and are
refused: a trailing dot on the root, a trailing dot, a leading dot and an
empty part on the local. X1 and X11 join the Cut 6b suite as entries,
each killed by that test.

The resolution arm's comment named "the 64-byte bound in local"; the
bound is LOCAL_MAX and the comment now says so.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-16 15:28:23 +02:00
Meta Vens
117f54b791 Close Cut 7
The parity manifest agrees with Eve's metadata, the fixture no longer names
Huginn anywhere, and AGENTS says the crates are stubs. Three small edits
with their checks pasted; no separate Soul pass, and the next Soul on
Huginn rereads AGENTS at Cut 8.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-16 15:26:29 +02:00
Meta Vens
ecf88f5020 Record Soul's Cut 7 pass and fix two map errors of Self's
Soul held the deletion line and the no-consumer claim, and found the fixture's
owner recorded in a third repo that now disagrees, three more Huginn-naming
fields, and AGENTS describing unbuilt cuts in the present tense. Two errors
were the map's own: a follow-up number used twice, and a negative grep that
ripgrep cannot parse.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-16 15:24:32 +02:00
Meta Vens
9d57a46079 Harness: restore inside every finally, sidecar the original, time out
Soul's second attack on the mutation harness found three ways a target
stayed mutated. A multi-target entry wrote its targets outside the try,
so a write that threw on the second target skipped the finally and left
the first mutated. M0's write-through had no finally at all. And a hard
kill mid-entry, which is what a tool timeout is, runs no finally, so the
target stayed mutated with nothing recording what it had been.

Every write now sits inside a try whose finally restores every target the
step touched, M0's included, each proven by hash; a target that cannot be
restored is printed with RESTORE FAILED and its original SHA-256 before
the run throws. Before any target is written its original bytes go to a
sidecar beside it, deleted only after a verified restore, and a run that
finds a sidecar at startup restores from it, verifies, removes it and
says a previous run died mid-mutation. Commands run through a Process
with a -TimeoutSeconds limit (1800 by default) and are killed with their
tree by the harness inside its own finally, so a hung cargo ends inside
the entry's restore rather than under the operator's tool.

Reproduced against a fake repo: A1 (M0 target write-locked) and A2 (the
second of two targets locked, in both hashtable orders) leave every
target byte-identical with no sidecar; A3 (tree killed mid-entry) leaves
the mutant and the sidecar, and the rerun reports the repair and kills
the entry; a 3-second timeout against a 12-second runner reports TIMED
OUT (no verdict) and restores.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-16 15:24:15 +02:00
Meta Vens
d094a19986 Correct the npm note: reachable after all, publishing deferred by choice
The operator reached npm signup today, so yesterday's blocked subnet is no
longer a Body fact. The ruling stands on a different reason: registry
publication is a later pass. Recorded in the operator's words beside the
earlier statement so the map does not carry a claim its author withdrew.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-16 15:18:10 +02:00
Meta Vens
79c5d38436 Record Soul's third pass: the grammar is closed, the harness is not
Every grammar pin held under non-revert mutations. What survived is the
harness under failure: writes outside the restoring finally, no guard on
the control's own write-through, nothing that survives a killed process,
and one reader fixture for trailing dots. A third batch takes those and
Cut 6c follows in the same tree.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-16 15:17:17 +02:00
Meta Vens
b359548241 Record Cut 7's landing on Huginn and Eve
Huginn's TypeScript body is gone and an empty Rust workspace stands in its
place; Eve's fixture says it is Eve's. The spec's file and line arithmetic
was off and is corrected in the landing note rather than the table, and the
conformance fixture's third Huginn-naming field is flagged for Soul.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-16 15:15:31 +02:00
Meta Vens
97fac26a32 Close Cut 7's npm question: nothing publishes, nothing unpublishes
The operator's ruling carries a Body fact the maps had not recorded: npmjs
is unreachable from this network, so no GameCult package can be published
there. Cut 7 needed no publish. The QUIC map's Q5 assumed one; that is
raised there separately.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-16 15:09:20 +02:00
Meta Vens
5670c76234 Record that Huginn lands before StreamPixels, not after
Operator correction: the CultLib QUIC cut may run without Huginn, but the
StreamPixels migration must run with it, because a portfolio piece should
demonstrate the full pipeline on typed state. The campaign no longer pauses
after Cut 6c; Cuts 7-16 follow, alongside QUIC where the repos allow it.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-16 15:05:53 +02:00
Meta Vens
ed09cd919e Record the second Cut 6b fix batch
The harness now restores from the original bytes on every path and checks
its own round trip before it writes; dotted roots and the reader's root-kind
check have tests and mutations; the local bound is one named constant. The
Cut 6 entry notes name the entries that actually pin their rules.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-16 15:01:05 +02:00
Meta Vens
f00062dbb1 Cut 6b: pin dotted roots, the reader's root kinds, and name the bound
Soul's second pass on the key grammar found four gaps, none in the
grammar itself: nothing pinned them.

No test keyed or read back a root with a dot in it, so both the
composer's root check and the reader's could have been a label check
(Soul's N9 and N10 survived). `dotted_roots_key_and_read_back` keys a
campaign `game.cult` and an instance `ygg.drasil`, reads each back by
`pipeline_id` recovering the root exactly, keys a target under the
dotted campaign and reads it back, and keys a resolution of the dotted
campaign as `game.cult:resolution:campaign.self`.

The reader's kind check was unpinned for the root kinds: excusing the
Instance kind from it survived (N6). `keys_read_back_as_ids_of_their_kind`
is extended, the first change to its body since Cut 6: an instance key
read as an instance succeeds, an instance key read as a campaign is
refused, and a campaign key read as an instance is refused.

The depth test restated the composer's bound as a literal, so moving
the bound and the test's 64s together survived (N4). `LOCAL_MAX` now
names the bound; `local` and the depth test both read it, and that
mutation is no longer a single-site edit. The Cut 6b entries that
anchored the literal, M4 and S5, anchor the name.

Cut 6's M5, M12 and M13 were deleted in d03a32df with notes pointing at
Cut 6b's M6 and S2, which pin neither the tail part nor the escape.
Soul's N3, N7 and N8 are those rules re-anchored: N3 skips the label
check on the last part of a local, N7 makes `key_segment` the identity,
N8 escapes `/` to a bare `_` with `_` unescaped. N6, N9 and N10 join
them in tools/eureka-cut6b-mutations.psd1, and the Cut 6 notes for M5,
M12, M13 and M15 now name the entries that pin them.

Every suite runs green through the harness: Cut 6b sixteen entries
killed (nine prior verdicts unchanged, N3, N6, N7, N8, N9, N10 killed),
Cut 6 ten killed, Cut 5 seven killed, M0 green in each. 21 tests in
epiphany-pipeline, no warnings. schemas/cultnet is unchanged.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-16 14:59:27 +02:00
Meta Vens
9370aa0f60 Harness: restore from bytes, count overlapping anchors
Soul's attack on tools/eureka-mutations.ps1 found two faults. M0 proved
the I/O path by writing the target and comparing hashes, so a broken
write path corrupted the target in the act of checking it, and the
harness could not put it back because it had only ever read text. And
"exactly once" counted non-overlapping matches, so an anchor that
overlaps itself, `}\n}\n` in `}\n}\n}\n`, counted as one site and the
splice at the first would have changed a region the entry does not
describe.

The harness now reads every target as bytes and keeps those bytes as
the restore source. M0 decodes, re-encodes through the one path every
write uses, and compares the result to the original bytes before
anything is written; a difference names the target and the first
differing offset, writes nothing, and runs no entry. Only then is the
target rewritten through the write path and hashed, with the original
bytes written back before the throw if the write lands other bytes.
After every entry the original bytes are written back and the file is
hashed against them; the offset reverse edit is gone, since the bytes
it reconstructed were in hand all along. Anchor sites are counted by an
IndexOf loop advancing one character per hit, so an overlapping anchor
is two and refused.

The usage comment records that -Test is split on whitespace with no
quoting, so a path with a space cannot be expressed.

Soul's attack script, re-targeted at the new Write-Text line: the
Set-Content variant, the BOM-encoder variant and the BOM-bearing second
target all leave every target byte-identical, the first two stopping at
M0 with no entry run; the overlapping anchor throws "matches 2 times".
The Cut 6b suite runs green through the fixed harness: M0 green, nine
entries killed.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-16 14:50:43 +02:00
Meta Vens
2a31ae0ab8 Record the first Cut 6b fix batch and Soul's harness attack
The four unpinned checks are pinned and the mutation harness is one script
with a built-in control. Soul then attacked the harness itself and found
the control could rewrite a target it cannot restore, the anchor guard
counted non-overlapping matches, dotted roots and the reader's root-kind
check were unpinned, and Self's own instruction to delete rather than
re-anchor five Cut 6 entries had cost the suite coverage it still claimed.
All of it is in a second fix batch.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-16 14:43:12 +02:00
Meta Vens
d03a32dfc1 One mutation harness for every Eureka cut
The Cut 5, 6 and 6b suites each carried the same scaffold around their
entries, and Cut 6c would have made a fourth. tools/eureka-mutations.ps1
is now the single harness: it takes an entries file, the target files
and the cargo command; reads and writes byte-exact UTF-8; requires every
anchor to match exactly once; applies each edit by offset and reverses
it by the same offset, verified by SHA-256; keeps a compile-must-fail
entry shape; and runs M0 before any entry, rewriting every target
through its own I/O path and running every command bare, stopping with
"harness broken" if anything moves or fails.

The three suites are now entries only, as .psd1 data files. Cut 6's M5,
M12, M13, M14 and M15 matched nothing after Cut 6b rewrote the grammar;
each is deleted with a note naming the Cut 6b entry that pins the rule
now. Cut 6b's file carries Soul's S2, S4 and S5, the mutations the
previous commit's tests kill.

All three suites run green through the harness: every entry killed,
M0 green.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-16 14:19:38 +02:00
Meta Vens
4b85dd2d0c Cut 6b: pin the four boundaries Soul's mutations survived
The nesting depth was described as "five" when the bound is the 64-byte
local: a chain n deep over a depth-one local of L bytes composes
11 * (n - 1) + L bytes, so `ruling.A` keys six deep and `question.Q1`
five. The arm comment and the test say the arithmetic, and the test
computes the deepest chain from it for both subjects and asserts the
first refusal.

The escape's `_` half was pinned only through an OrgRepo; a receiver
`a_db` and a receiver `a.b` now key differently, the former through
`a__db`. The composer's root check was pinned by `a:b` alone; a trailing
dot, a leading dot, an empty part and a 65-byte root are each refused
by `pipeline_key` without `validate()`. The whole-local bound was
unpinned on both sides; a 64-byte local keys and a 65-byte one refuses,
for a plain label and for an escaped repo.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-16 14:06:29 +02:00
Meta Vens
103f885afa Record Cut 6b's landing and Soul pass, and fold in the Cut 6c spec
Cut 6b landed in three commits and Soul's whole-key probe found no collision
across 212,450 adversarial keys. Three checks were unpinned (the underscore
half of the escape, the composer's root check, the 64-byte boundary) and are
in a fix batch with Soul's own surviving mutants as their killers. Two map
claims were wrong and are corrected: nesting depth follows from the subject's
local rather than being five, and a validated date passes raw because it is
already a label.

The Cut 6c spec is folded in as reissued against the landed grammar, with the
supersession fixture only the new grammar makes nameable and the mutation
entries pointed at the shared harness the fix batch is factoring out.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-16 14:03:34 +02:00
Meta Vens
95ee551aa0 Cut 6b: mutation suite for the key grammar
Six mutations, each restoring one of the old permissivenesses the grammar
made impossible, and M0, a no-op control that rewrites the file through
the harness's own I/O path and requires the suite to stay green before
any verdict is trusted. All file I/O is byte-exact UTF-8 without BOM,
since the file carries an `é` literal that the 5.1 default round-trip
corrupts into a fake kill. Every anchor must match exactly once, multi-line
anchors are converted to the file's own line endings, restoration is the
reverse edit checked against the original's hash, and a mutation whose
tree does not build is an error rather than a survival.

Result on this tree: M0 green; M1-M6 each killed by its named test.

This commit builds; it changes no Rust source.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-16 13:45:39 +02:00
Meta Vens
1bddd2acb3 Cut 6b: one key grammar for every kind
Every key is now `<root>:<kind>:<local>`, three segments with no kind
excused. The root is a `Slug`, the kind is its literal name, and the
local is `Label`s joined by `.`, so no part carries the separator and a
reader recovers the parts by splitting. Roots take the constant local
`self`; a resolution is keyed inside its subject's root with the subject's
kind and local as its own local, so a resolution of a resolution is an
ordinary id bounded by the 64-byte local and needs no guard.

- `local` is the one composer: every part through `label_text`, the join
  bounded whole, once. `pipeline_key` has thirteen arms and one exit.
- `pipeline_id` is the grammar read backwards, with no per-kind branch.
- `repo_segment` becomes `key_segment`: the same injective escape, now for
  any `Slug` or `OrgRepo` entering a local. `hand_off.to_instance`, the
  only raw `Slug` that reached a local, goes through it.
- `parent_cut` indexes the split parts instead of splitting from the right.

Every key moves, and nothing outside this file reads one. No schema file,
kind, type, field, dependency or epoch changes.

Tests: six new (`every_key_has_exactly_three_segments`,
`roots_of_different_kinds_do_not_share_a_key`,
`a_resolution_names_its_subjects_kind`,
`a_resolution_of_a_resolution_reads_back`,
`a_composed_local_is_bounded_whole`,
`no_local_part_carries_the_separator`); thirteen expected keys move in
the samples; `composed_keys_cannot_collide`'s composer probe moves into the
separator test and its dotted receiver now escapes.

Spec deviations, in the report: `local` takes `&[&str]` rather than a
const array because the resolution and finding arms carry a parent's
parts; the whole-local bound refuses `InvalidFormat` to match the spec's
own test and the reader's refusal of the same string; `key_segment` is a
pure escape and each caller validates its source type by its own field.

This commit builds and its tests pass.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-16 13:43:09 +02:00
Meta Vens
fcf1ce6bdf Map Cut 6b, the key grammar, and record the pause after Cut 6c
Three fix passes each made one key composition site injective and left the
site above it open. Soul's third pass found campaign and instance sharing a
root namespace, a resolution key discarding its subject's kind, and nested
resolutions composing an unreadable key. That is a missing owner, not three
bugs, so the grammar is redesigned as Cut 6b: root:kind:local, three segments
always, no local part a slug, roots keyed with the constant self. The
exceptions are deleted, not guarded.

The Ghostlight shapes become Cut 6c and land after 6b, once Imagination
reissues them against the settled grammar. The campaign pauses after 6c; the
QUIC realtime plane in CultLib goes first because StreamPixels is the
portfolio piece, and Huginn resumes after it ships.

Also recorded: the second Cut 6 fix batch and the findings it closed; the
mutation-harness audit, which found the corrupting round-trip in no committed
suite but no no-op control in any suite either; corrections 19-24 (the dot
escape, the unstated resolution key, the std::process grep, the ruled
target-dir deviation, the second byte-identical claim, the README's foreign
consumer promise); and Q10-Q12 with recommendations.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-16 13:39:53 +02:00
Meta Vens
602ffd9fcf Cut 6b: delete the three key-shape exceptions
The key space had four shapes because three of thirteen kinds returned a
key the composer never saw, and the reader undid them with a per-kind
match and a kind-guessing helper. Every confirmed defect lived in one of
those exceptions. This commit removes them before anything replaces them,
so no commit holds both the exceptions and the new composer:

- `declared_kind`, the inference that hardcoded `Campaign` for a bare slug
- the `match kind` block in `pipeline_id` (root arm, resolution arm and
  its recursion)
- `KeyParts`, its constructors, `key`, and the "only the head may carry
  the separator" doctrine comment
- the three early `return`s in `pipeline_key` (instance, campaign,
  resolution)

This commit does not build: `pipeline_key` references `KeyParts` and the
tests reference the deleted shapes. The next commit lands the grammar.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-16 13:39:39 +02:00
Meta Vens
4a654351e3 Say what the per-run schema directory costs
Soul G3: the comment asserted only the half that improved -- a passing
run leaves nothing -- and was silent about the failing run, which is the
only case that creates a directory at all and now leaks one permanently,
one per failing run where the shared directory was overwritten however
often it failed. The trade is still right; the comment now names its
cost and says who cleans up.

Comment only. No behaviour, so no mutation.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-16 12:53:57 +02:00