Follows the source cut: the OrgRepo doc comment (RS-L closing fix 2) now
publishes only the grammar, the 140-byte bound and the case-insensitive
identity, with the bounded_text!/hand-written-impl reasoning moved to a
source comment. Regenerated with cargo test's own derivation so the
byte-for-byte pin (pipeline_published_schemas_match_derivation) stays green:
campaign, cut_report, cut_spec, finding, follow_up, hand_off, ruling and
stewardship all embed OrgRepo (directly or through ForeignRef's repo field).
instance, question, resolution, target and verdict do not reference OrgRepo
and are unchanged.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
OrgRepo's doc comment now explains its hand-written identity-based
PartialEq/Eq/Hash/PartialOrd/Ord and the ASCII-case-insensitive .git
refusal (S-1, S-2); Title's regains the bytes-versus-characters caveat
(S-5). Both are schema descriptions, so nine of the thirteen published
schemas move: campaign, question, ruling and cut_spec carry Title;
campaign, cut_report, cut_spec, finding, follow_up, hand_off, ruling and
stewardship carry OrgRepo directly or through ForeignRef. Byte-for-byte
identical to what `schemars::schema_for!` derives from the code in the
previous commit, pinned by pipeline_published_schemas_match_derivation.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Derived on Yggdrasil from the rewritten lib.rs by running
pipeline_published_schemas_match_derivation, which writes every stale
derivation to a temp directory on failure; those nine files are copied back
here byte for byte. Re-running the test afterward passes clean (0 stale).
Nine of the thirteen published kinds change: campaign, question, ruling and
cut_spec carry Title's new description; cut_report, finding, follow_up,
stewardship and hand_off carry OrgRepo's new description and its maxLength
move from 200 to 140. instance, resolution, target and verdict reference
neither type and are untouched.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Title now refuses a bare placeholder: at least one non-whitespace
character, and no control characters (C0, C1, U+2028/9, the zero-width
characters, or the BOM). The 1-to-200-byte bound is unchanged, and the
Title schema now carries a truthful minLength/description pair instead
of the old ruling-history prose (F2, F3, F4).
OrgRepo now follows GitHub's own owner/repo grammar: an owner of
[A-Za-z0-9-], 1-39 bytes, no leading or trailing hyphen; a repo of
[A-Za-z0-9._-], 1-100 bytes, never "." or ".." (F7). Two fixtures used
an OrgRepo the new grammar refuses (a dotted owner, an underscored
owner); both are rewritten to carry the same character on the repo
side of the slash, which the grammar still allows, with their expected
keys recomputed.
Regenerated the four published schemas (campaign, cut_spec, question,
ruling) that reference Title, pinned by the byte-for-byte derivation
test.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Q-RS1 ruled B: a new bounded text type, Title (1-200 bytes, never empty),
replaces Short on all four leaf titles -- campaign, cut spec, and the two
new fields on question and ruling. The epoch moves anyway in this cut, so
tightening the two existing titles now avoids a second epoch bump later
just to make a title required.
PIPELINE_SCHEMA_EPOCH and all thirteen type ids move to .v2. The thirteen
schema files are renamed and regenerated, and index.json/README.md follow.
Two public doors join Slug::validate_slug on the same pattern:
OrgRepo::validate_org_repo and Label::validate_label, each delegating to
the grammar its own document field is already held to. RS-3 will read the
organ's repo and cut aliases through these.
Tests: the_org_repo_and_label_doors_are_the_grammar and
question_and_ruling_carry_a_title (37 total, up from 35). Mutation suite
tools/eureka-readside-leaf-mutations.psd1 (L1/L1L, L2/L2L, L3/L3L) verified
killed against M0.
No other consumer in this workspace depends on epiphany-pipeline as a
Cargo dependency; epiphany-core's runtime_spine stand-in hardcodes a
pipeline-shaped type id deliberately (RS-L's negative grep excludes it) and
is unaffected since its test only checks the "epiphany.pipeline." prefix.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
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>
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>
The operator ruled on 2026-09-16 that widening a `PipelineKind` enum is
additive. The README's additive rule named only a new field with a serde
default, which is true and stays, but did not cover the case.
The clause says why in the README's own voice: these readers are ours and each
ships with the variants it knows, so a reader that meets a kind it has never
heard of refuses it on the kind, not on the epoch. That is also the answer to
the tension with the wire note two sentences earlier -- the contracts do cross
out of this repo, and the rule holds because the readers on the other side are
still ours. If that stops being true, the rule needs revisiting.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The Main Families entry still said pipeline state is "stored per repo at
`.epiphany/pipeline/pipeline.cc`", which Cut 4 deleted, and the wire note still
sent these contracts to the voidbot projection. Both describe a machine that no
longer exists.
Pipeline state now lives in an instance's mind, owned by the Huginn memory
organ, and the contracts cross to that organ and its `eureka-state` client. The
three kinds this branch added are named, since the list is the entry's point.
The `[value]` payload sentence, the derivation sentence and the Q2 evolution
sentence stand; the derivation sentence now says which package runs the check.
Documentation only: no build, no test.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Ruling 1 keeps schema ownership in Epiphany; ruling 17 puts admission in
Huginn. Huginn must not take on epiphany-core -- about 41,000 lines and 295
lock packages -- to use about 1,000 lines of document types. So the documents
leave as a leaf library, and epiphany-core keeps no pipeline surface at all:
its consumption is campaign two.
`epiphany-core/src/pipeline_documents.rs` moves whole to
`epiphany-pipeline/src/lib.rs`, with its nine tests. Only three things changed
in the move: the module doc now describes a package rather than a cut map
section; `anyhow` joins `rmp-serde` behind `cfg(test)`, because the write path
has been test-only since 186dbc62 and the live library's errors are all
`PipelineRefusal`; and the decode refusal, which was pinned against
epiphany-core's Mind commit receipt, is pinned against a local `ForeignDocument`
carrying that receipt's type id, since the receipt belongs to the harness.
The package declares four dependencies. chrono parses a Date, cultcache-rs
supplies DatabaseEntry, schemars derives the published schemas (Q5), and serde
is the document format. anyhow, rmp-serde and serde_json are dev-dependencies:
every use of them is `cfg(test)`. `autobins = false`, as a leaf library must.
The workspace lock grows by exactly one entry, the member itself; no new
third-party package.
Deleted rather than re-exported: epiphany-core's `mod pipeline_documents;` and
`pub use pipeline_documents::*;`, and `schemars` from its manifest, whose only
user was this module. No shim, no duplicate type.
`runtime_spine_cache_refuses_a_pipeline_store` stays in epiphany-core, because
the rule is the spine registry's: it registers its own types and nothing else.
It now builds its foreign envelope from a `cfg(test)` stand-in carrying
`epiphany.pipeline.campaign.v1` rather than from the moved documents, so the
rule keeps its test without the package keeping a dependency.
D2's three new kinds arrive with the move, each with a live consumer in the
organ: `instance` is a mind's identity document, `stewardship` records the
repos an instance is assigned, and `hand_off` records a reassignment in both
minds. All three hang off an instance rather than a campaign, so they compose
their own keys instead of borrowing the `<campaign>:<kind>:<local>` tail. A
repo inside a key is escaped to `Org_Repo`, because `/` is not a Label byte.
Thirteen schemas now derive. The five that embed `PipelineKind` gained three
enum variants, which is additive, so Q2 keeps `epiphany.pipeline.epoch.v1`.
epiphany-pipeline: 12 tests, 0 warnings. epiphany-core: 163 tests -- 172 minus
the nine that moved -- 0 warnings. epiphany-state.exe is byte-identical.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
One store per clone, numeric attempt and pass, and identity enforced by the
profile that owns the commit. The three files change together because the
profile's validator signature, the documents that implement it and the store
that installs it are one interlocking contract; no smaller split builds.
Ruling 10, one store per clone. `main_work_tree` resolves the clone's main
working tree from any worktree through the git common dir, and both
`PipelineStore::open` and `PipelineWriter::attach` go through it, so a caller
in a worktree reads and admits into the main tree's store. A bare clone or any
layout without one main tree refuses `NoMainWorkTree`; there is no
worktree-only mode. `require_branch` binds to the main tree, so a campaign
whose branch is checked out only in a linked worktree is refused rather than
quietly given a second store.
Ruling 11, numeric labels. `attempt` and `pass` are `u32`, keyed
`cut-<cut>.h<N>` and `cut-<cut>.s<N>`.
Ruling 12, identity on first write. `TypedCommitStore` now validates a whole
batch against the image it will be applied to (`validate_writes`), replacing
the per-envelope hook rather than adding a second one. The pipeline profile
refuses a first write that does not carry the identity, so the single owner can
never admit a store its own opener refuses (F1).
F2: a finding label carries no dot and parent locals parse strictly, so two
documents cannot compose one key. Soul's collision pairs are tested.
F3: parent ids are parsed with an exact segment count, a campaign match, a kind
match, a validated local, and a marker followed by digits.
F4: a resolution's subject id is validated as a full id of its declared kind,
by `PipelineRef` itself, so kind and id cannot disagree anywhere.
F5: the ten document wrappers, the identity, provenance and holder wrappers and
their value fields are crate-private, and the crate's only commit path to a
pipeline store is a `cfg(test)` helper taking `&PipelineWriter`. Binding the
owner to the lease is Cut 3b.
F6: covered by ruling 10, with the previous claim corrected: a worktree does
not keep its own store, and a second attach from any tree is refused.
F7: the holder record is written only under the lock and removed on drop, and a
refusal names a holder only when it is plausibly current, matching host and
live process incarnation. A record left by a crash names nobody.
F8: the git checks read the committed blob at HEAD, so they answer the same on
every machine. Attribute and exclude files are nulled on every git child. The
working tree and `.git/info/exclude` deliberately do not count, because the rule
is about what the repository commits.
Low items: `value_types!` is the single field list, emitting each struct and its
`Bounded` impl from the same tokens, and format rules moved into the field types
(`Label`, `Slug`, `OrgRepo`, `FullSha`, `Sha256Hex`), so a field cannot be
declared without being validated and a list without a cap does not compile. That
applies the `Org/Repo` check to every repo field. `LandedNameKind` is deleted and
`LandedName` is a name and a path. The schemas carry `maxLength` and `maxItems`.
Builds clean with no warnings; `cargo test -p epiphany-core --lib` passes
179/179.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Cut 3a of the Eureka pipeline state campaign (cut map D1-D3).
- pipeline_documents.rs: the ten kinds as serde + JsonSchema values in
one-slot DatabaseEntry wrappers (epiphany.pipeline.<kind>.v1), the
identity, provenance and writer-holder documents, UTF-8 byte bounds,
semantic key derivation, and the pipeline write validator (bounds, then
key recomputation).
- pipeline_store.rs: PipelineStore::open (read-only, no lease),
PipelineWriter::attach (fs2 lock in the git common dir, display-only
holder file, binary/ignore checks), require_branch, PipelineRefusal,
and PIPELINE_COMMIT_STORE on the one commit owner. The opener refuses
foreign types, missing identity and foreign epochs before attaching.
- schemars 1 is an unconditional core dependency (Q5-A); the ten published
schemas are derived and checked byte-for-byte, with index.json entries.
The profile and the prepare path carry expect(dead_code) naming Cut 3b as
their consumer, so the attribute fails the build once admission uses them.
Builds: yes. cargo test -p epiphany-core --lib: 171/171, 0 warnings.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>