Rename the pipeline leaf to eureka-pipeline and take it as a path dependency

Package and lib become eureka-pipeline / eureka_pipeline; the crate joins the
workspace; huginn-mind's git dependency on GameCult/Epiphany is replaced by
the path, and Cargo.lock is regenerated (the Epiphany source entry is gone).
Every epiphany_pipeline mention in huginn-mind, huginn-daemon and eureka-state
is renamed. The schema-publication test reads ../../schemas/cultnet. The
leaf's license-file key (../LICENSE, which Huginn has no file for) is dropped.
Type ids, the epoch and every schema_id string are untouched. Schemas README
and the pipeline section of schemas/cultnet/README.md are rewritten for the
new home; README and AGENTS no longer say the leaf is pinned by git. New test:
the catalogue lists exactly the schema files beside it.

Not yet built or tested; verification follows on Yggdrasil.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Meta Vens 2026-10-09 14:09:16 +02:00
commit c46b0ec183
29 changed files with 150 additions and 107 deletions

View file

@ -11,15 +11,17 @@ Studio's job, not Huginn's.
- Project root: `F:\Projects\Huginn`
- Upstream: `https://github.com/GameCult/Huginn.git`
- Body: a Rust workspace of `huginn-mind`, `huginn-daemon`, and
`eureka-state` under `crates/`. Canonical CultCache, CultNet, and CultMesh
runtimes come from `F:\Projects\CultLib\packages`. `huginn-mind` is live:
it persists, admits and reads back an instance's mind, queries and derived
status included, over `cultcache-rs` and `epiphany-pipeline` pinned by git
rev. `huginn-daemon` serves one mind over CultNet RUDP through `cultnet-rs`
at the same rev, and reaches the leaf and the store type through
`huginn-mind` so one crate pins each revision. `eureka-state` is the client
core: one call to a daemon over CultNet, holding no state.
- Body: a Rust workspace of `eureka-pipeline`, `huginn-mind`, `huginn-daemon`,
and `eureka-state` under `crates/`. Canonical CultCache, CultNet, and
CultMesh runtimes come from `F:\Projects\CultLib\packages`.
`eureka-pipeline` is the pipeline leaf: document shapes, bounds, formats,
keys and the schemas published under `schemas/cultnet`. `huginn-mind` is
live: it persists, admits and reads back an instance's mind, queries and
derived status included, over `cultcache-rs` and `eureka-pipeline`.
`huginn-daemon` serves one mind over CultNet RUDP through `cultnet-rs` at
the same rev, and reaches the leaf and the store type through `huginn-mind`
so one crate pins each revision. `eureka-state` is the client core: one call
to a daemon over CultNet, holding no state.
- Owned: an instance's memory documents, their admission and the read side
that derives their status, in `huginn-mind`,
over a redb CultCache store at `<state_root>/minds/<instance>/mind.redb`;

28
Cargo.lock generated
View file

@ -372,19 +372,6 @@ dependencies = [
"zeroize",
]
[[package]]
name = "epiphany-pipeline"
version = "0.1.0"
source = "git+https://github.com/GameCult/Epiphany.git?rev=ef9568655e75bb6cb362a038e2739a5ac5e777ee#ef9568655e75bb6cb362a038e2739a5ac5e777ee"
dependencies = [
"anyhow",
"chrono",
"cultcache-rs",
"rmp-serde",
"schemars",
"serde",
]
[[package]]
name = "errno"
version = "0.3.14"
@ -395,6 +382,19 @@ dependencies = [
"windows-sys 0.61.2",
]
[[package]]
name = "eureka-pipeline"
version = "0.1.0"
dependencies = [
"anyhow",
"chrono",
"cultcache-rs",
"rmp-serde",
"schemars",
"serde",
"serde_json",
]
[[package]]
name = "eureka-state"
version = "0.1.0"
@ -644,7 +644,7 @@ dependencies = [
"chrono",
"cultcache-rs",
"cultnet-rs",
"epiphany-pipeline",
"eureka-pipeline",
"rmp-serde",
"schemars",
"serde",

View file

@ -1,5 +1,6 @@
[workspace]
members = [
"crates/eureka-pipeline",
"crates/huginn-mind",
"crates/huginn-daemon",
"crates/eureka-state",

View file

@ -61,7 +61,7 @@ It reads them back the same way: a document with the facts of its admission
joined from that receipt and its status derived at read time, and typed
queries over one mind. Status is never stored, and the views derive it
through the same rules admission does. Document shape and keys come from
`epiphany-pipeline`; the store is CultLib's Rust CultCache.
`eureka-pipeline` (`crates/eureka-pipeline`); the store is CultLib's Rust CultCache.
`huginn-daemon` opens one mind, binds one UDP socket in that order, and
answers every frame on the session it arrived on: one operation per `Mind`

View file

@ -1,13 +1,12 @@
[package]
name = "epiphany-pipeline"
name = "eureka-pipeline"
version = "0.1.0"
edition = "2024"
publish = false
autobins = false
license-file = "../LICENSE"
[lib]
name = "epiphany_pipeline"
name = "eureka_pipeline"
path = "src/lib.rs"
# A leaf type library. Every dependency here is one the document types or the

View file

@ -1094,7 +1094,7 @@ mod tests {
}
fn location() -> CodeLocation {
CodeLocation { path: s("epiphany-pipeline/src/lib.rs"), line: 1, end_line: Some(9) }
CodeLocation { path: s("crates/eureka-pipeline/src/lib.rs"), line: 1, end_line: Some(9) }
}
fn evidence() -> Evidence {
@ -1187,7 +1187,7 @@ mod tests {
dependencies_removed: vec![], formats_added: vec![s("epiphany.pipeline.*.v1")],
formats_removed: vec![], targets_added: vec![], targets_removed: vec![],
},
landed_names: vec![LandedName { name: s("PipelineDocument"), path: s("epiphany-pipeline/src/lib.rs") }],
landed_names: vec![LandedName { name: s("PipelineDocument"), path: s("crates/eureka-pipeline/src/lib.rs") }],
undone: vec!["admission".into()],
promises: vec![Promise { label: l("P1"), text: "One derived key per document.".into() }],
}), format!("{CAMPAIGN}:cut_report:cut-3a.h1")),
@ -2722,7 +2722,7 @@ mod tests {
#[test]
fn pipeline_published_schemas_match_derivation() -> Result<()> {
let published = Path::new(env!("CARGO_MANIFEST_DIR")).join("../schemas/cultnet");
let published = Path::new(env!("CARGO_MANIFEST_DIR")).join("../../schemas/cultnet");
let index: serde_json::Value = serde_json::from_slice(&std::fs::read(published.join("index.json"))?)?;
// A directory this run alone writes, so everything in it is what this
// run derived. A previous run's leftovers -- a mutation run's
@ -2739,7 +2739,7 @@ mod tests {
// they outlive the run that wrote them. Whoever reads the failure is
// the one who deletes them.
let derived_dir = std::env::temp_dir().join(format!(
"epiphany-pipeline-schemas-{}-{}",
"eureka-pipeline-schemas-{}-{}",
std::process::id(),
std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH)?.as_nanos()
));
@ -2772,6 +2772,36 @@ mod tests {
Ok(())
}
/// The catalogue is exactly the files beside it: every entry's `path` is a
/// schema file in the directory, and every schema file in the directory
/// has an entry, so a schema removed from the catalogue (or a file left
/// behind by a move) fails here instead of publishing a dangling or
/// unlisted contract. Schemas whose owner is another crate (`huginn.*`)
/// are published by that crate, not listed here.
#[test]
fn the_catalogue_lists_exactly_the_schema_files_it_publishes() -> Result<()> {
let published = Path::new(env!("CARGO_MANIFEST_DIR")).join("../../schemas/cultnet");
let index: serde_json::Value = serde_json::from_slice(&std::fs::read(published.join("index.json"))?)?;
let listed = index["schemas"]
.as_array()
.expect("index.json carries a schemas array")
.iter()
.map(|entry| entry["path"].as_str().expect("every entry names its file").to_owned())
.collect::<std::collections::BTreeSet<_>>();
for path in &listed {
assert!(published.join(path).is_file(), "index.json lists {path}, which is not in the directory");
}
let mut files = std::collections::BTreeSet::new();
for entry in std::fs::read_dir(&published)? {
let name = entry?.file_name().to_string_lossy().into_owned();
if name.ends_with(".schema.json") && !name.starts_with("huginn.") {
files.insert(name);
}
}
assert_eq!(listed, files, "the catalogue and the schema files beside it disagree");
Ok(())
}
/// The epoch is the version every kind is published at: its version
/// segment is read from the constant, not restated, and every type id
/// carries the same one, so a bumped epoch fails here until the kinds

View file

@ -26,7 +26,7 @@ use cultnet_rs::{
CultNetRudpSocketTransportConnection, CultNetWireContract, encode_cultnet_message_to_vec, fetch_content,
};
use huginn_mind::envelope::{OperationFailure, decode_response, encode_request};
use huginn_mind::epiphany_pipeline::Slug;
use huginn_mind::eureka_pipeline::Slug;
use huginn_mind::{HuginnMindRequest, HuginnMindResponse, MAX_DEFERRED_BODY_BYTES};
/// The correlation key of the one request a session carries.

View file

@ -22,7 +22,7 @@ use std::time::Duration;
use cultnet_rs::Selection;
use eureka_state::{ClientError, HuginnClient};
use huginn_mind::epiphany_pipeline::{PipelineDocument, PipelineRef, Short, Slug};
use huginn_mind::eureka_pipeline::{PipelineDocument, PipelineRef, Short, Slug};
use huginn_mind::{
Faculty, HuginnMindRequest, HuginnMindResponse, PipelineAdmissionBatch, PipelineProvenance, SemanticQuery,
};

View file

@ -14,7 +14,7 @@ use cultnet_rs::{
use eureka_state::{ClientError, HuginnClient, MAX_REQUEST_BYTES};
use huginn_daemon::serve::MAX_RESPONSE_BYTES;
use huginn_mind::envelope::{OperationFailure, encode_failure, encode_request, encode_response};
use huginn_mind::epiphany_pipeline::{
use huginn_mind::eureka_pipeline::{
AuthorityMap, CodeLocation, CutDelete, CutVerification, FileChange, Line, NegativeCheck, OrgRepo,
PipelineCutSpec, PipelineDocument, PipelineKind, PipelineRef, Short, StructuralDelta, Title,
VerificationTest,

View file

@ -13,7 +13,7 @@ use chrono::Utc;
use cultnet_rs::{CultNetMessage, CultNetRudpServerEvent, CultNetWireContract, decode_cultnet_message_from_slice};
use huginn_daemon::serve::{bind, run, schema_registry};
use huginn_daemon::{Daemon, Handled, Hits, IndexSink, SearchTicket, ServeOptions};
use huginn_mind::epiphany_pipeline::{
use huginn_mind::eureka_pipeline::{
Date, DocRef, OrgRepo, PipelineCampaign, PipelineDocument, PipelineInstance, PipelineRef, PipelineStewardship, Sha, Short, Slug,
Title,
};

View file

@ -11,7 +11,7 @@ use std::time::Duration;
use huginn_mind::envelope::{OperationFailure, encode_failure, encode_response};
use huginn_mind::{HuginnMindRequest, HuginnMindResponse};
use huginn_mind::epiphany_pipeline::{Date, PipelineDocument, PipelineKind, PipelineRef, Short};
use huginn_mind::eureka_pipeline::{Date, PipelineDocument, PipelineKind, PipelineRef, Short};
use serde_json::{Value, json};
mod common;
@ -536,7 +536,7 @@ fn initialize_advertises_tools() {
/// value `pipeline_key` computes for that document, never a re-built one.
#[test]
fn admit_carries_the_leaf_key_refusal_with_its_field_and_value() {
use huginn_mind::epiphany_pipeline::{PipelineResolution, ResolutionOutcome, pipeline_key};
use huginn_mind::eureka_pipeline::{PipelineResolution, ResolutionOutcome, pipeline_key};
let (root, daemon) = mind(vec![vec![stewardship()]]);
let server = serve(root, daemon);

View file

@ -4,7 +4,7 @@
use chrono::{DateTime, Utc};
use cultnet_rs::Selection;
use huginn_mind::epiphany_pipeline::{PipelineRef, Slug};
use huginn_mind::eureka_pipeline::{PipelineRef, Slug};
use huginn_mind::wire::{HuginnMindRequest, HuginnMindResponse, IndexStatus};
use huginn_mind::{Mind, MindRefusal, MindStore, PipelineAdmissionOutcome, SemanticQuery};
@ -217,7 +217,7 @@ impl<S: MindStore, I: IndexSink<S>> Daemon<S, I> {
pub(crate) mod tests {
use super::*;
use chrono::TimeZone;
use huginn_mind::epiphany_pipeline::{
use huginn_mind::eureka_pipeline::{
AuthorityMap, CodeLocation, CutDelete, CutVerification, Date, DocRef, FileChange, Line, NegativeCheck,
OrgRepo, PipelineCampaign, PipelineCutSpec, PipelineDocument, PipelineInstance, PipelineKind,
PipelineQuestion, PipelineRuling, PipelineStewardship, QuestionOption, RulingAuthority, Sha, Short,
@ -685,7 +685,7 @@ pub(crate) mod tests {
assert_ne!(fullwidth, INSTANCE, "not the mind's own bytes");
assert!(!fullwidth.is_ascii(), "outside the grammar: a Slug is dot-joined ASCII labels");
let expected = MindRefusal::Document(huginn_mind::epiphany_pipeline::PipelineRefusal::InvalidFormat {
let expected = MindRefusal::Document(huginn_mind::eureka_pipeline::PipelineRefusal::InvalidFormat {
field: "declared".into(),
value: fullwidth.into(),
});
@ -715,7 +715,7 @@ pub(crate) mod tests {
let (_root, mut daemon) = seeded();
let fullwidth = "\u{FF59}ggdrasil";
let expected = MindRefusal::Document(huginn_mind::epiphany_pipeline::PipelineRefusal::InvalidFormat {
let expected = MindRefusal::Document(huginn_mind::eureka_pipeline::PipelineRefusal::InvalidFormat {
field: "declared".into(),
value: fullwidth.into(),
});
@ -744,7 +744,7 @@ pub(crate) mod tests {
// A reference whose kind and id disagree names no document any writer
// could have composed; the leaf's own refusal says so.
let invalid = PipelineRef { kind: PipelineKind::Question, id: "not a reference".into() };
let malformed = MindRefusal::Document(huginn_mind::epiphany_pipeline::PipelineRefusal::InvalidFormat {
let malformed = MindRefusal::Document(huginn_mind::eureka_pipeline::PipelineRefusal::InvalidFormat {
field: "ref.id".into(),
value: "not a reference".into(),
});

View file

@ -99,7 +99,7 @@ use std::sync::{Arc, Condvar, Mutex, MutexGuard};
use std::time::{Duration, Instant};
use anyhow::{Context, Result, bail, ensure};
use huginn_mind::epiphany_pipeline::{PipelineKind, PipelineRef, Short, Slug};
use huginn_mind::eureka_pipeline::{PipelineKind, PipelineRef, Short, Slug};
use huginn_mind::wire::IndexStatus;
use huginn_mind::{INDEX_TEXT_VERSION, IndexEntry, Mind, MindStore};
use sha2::{Digest, Sha256};

View file

@ -10,7 +10,7 @@ use std::time::Duration;
use anyhow::Result;
use cultnet_rs::{CULTNET_OPERATION_CONNECTION_ID, CultMesh, CultMeshRudpSocketOptions, FieldPredicate, Selection};
use huginn_mind::epiphany_pipeline::{
use huginn_mind::eureka_pipeline::{
Date, PipelineDocument, PipelineKind, PipelineQuestion, PipelineResolution, PipelineRef, PipelineRuling,
QuestionOption, ResolutionOutcome, RulingAuthority, Short, Title,
};

View file

@ -68,7 +68,7 @@ use cultnet_rs::{
CultNetWireContract, answer_content_chunk_request, decode_cultnet_message_from_slice,
encode_cultnet_message_to_vec, pack_content,
};
use huginn_mind::epiphany_pipeline::Slug;
use huginn_mind::eureka_pipeline::Slug;
use huginn_mind::wire::{
DeferredAnswer, HuginnMindResponse, MIND_REQUEST_SCHEMA, MIND_REQUEST_SCHEMA_JSON, MIND_RESPONSE_SCHEMA,
MIND_RESPONSE_SCHEMA_JSON, MIND_SERVICE_ID,
@ -548,7 +548,7 @@ mod tests {
FITTING_CHANGES, FITTING_CUT, INSTANCE, NoIndex, OTHER, WIDE_CHANGES, WIDE_CUT, batch, identity, now,
open_unindexed, seeded_wide, slug,
};
use huginn_mind::epiphany_pipeline::{PipelineKind, PipelineRef};
use huginn_mind::eureka_pipeline::{PipelineKind, PipelineRef};
use huginn_mind::envelope::{FAILURE_SCHEMA, decode_response, encode_request};
use crate::index::Backoff;
use base64::Engine;

View file

@ -6,9 +6,10 @@ license.workspace = true
publish.workspace = true
# The organ's rule engine: storage, identity and admission of one instance's
# mind. Document shape, bounds, formats and keys come from `epiphany-pipeline`;
# the store comes from CultLib's Rust CultCache at the same rev Epiphany pins,
# so the leaf's wrappers and this crate's cache are one `cultcache-rs`. The
# mind. Document shape, bounds, formats and keys come from this workspace's
# `eureka-pipeline` (crates/eureka-pipeline, copied once from Epiphany ef956865);
# the store comes from CultLib's Rust CultCache at the rev `eureka-pipeline`
# also uses, so the leaf's wrappers and this crate's cache are one `cultcache-rs`. The
# read side selects through CultNet's typed selection (`cultnet-rs`, the same
# rev the daemon pins): the selection's vocabulary and evaluator are the
# substrate's, this crate supplies the rows. No socket, no index: those are the
@ -19,7 +20,7 @@ base64 = "0.22"
chrono = "0.4.44"
cultcache-rs = { git = "https://github.com/GameCult/CultLib.git", rev = "8fc74c709444b884a2bd339e0c306bf9484d99d2", package = "cultcache-rs" }
cultnet-rs = { git = "https://github.com/GameCult/CultLib.git", rev = "8fc74c709444b884a2bd339e0c306bf9484d99d2", package = "cultnet-rs" }
epiphany-pipeline = { git = "https://github.com/GameCult/Epiphany.git", rev = "ef9568655e75bb6cb362a038e2739a5ac5e777ee", package = "epiphany-pipeline" }
eureka-pipeline = { path = "../eureka-pipeline" }
rmp-serde = "1"
schemars = "1"
serde = { version = "1", features = ["derive"] }

View file

@ -30,7 +30,7 @@ use std::collections::BTreeSet;
use chrono::{DateTime, Utc};
use cultcache_rs::{CultCache, CultCacheEnvelope};
use epiphany_pipeline::{
use eureka_pipeline::{
ClaimOutcome, FindingConfidence, Line, PipelineDocument, PipelineKind, PipelineRef, PipelineRefusal,
PipelineResolution, PipelineStewardship, ResolutionOutcome, RulingAuthority, Short, Slug, pipeline_key,
validate_pipeline_write_envelope,
@ -180,7 +180,7 @@ impl<S: MindStore> Mind<S> {
}
}
/// The bound `epiphany_pipeline::Short` declares, in UTF-8 bytes. The leaf
/// The bound `eureka_pipeline::Short` declares, in UTF-8 bytes. The leaf
/// exposes no validator for it, so `short_bound_is_the_leafs_own` pins this to
/// the `maxLength` its schema publishes.
const SHORT_MAX: usize = 200;
@ -371,7 +371,7 @@ fn check(docs: &Docs, staged: &Staged, mind: &Slug) -> Result<(), MindRefusal> {
Ok(())
}
D::Target(target) => {
let predecessor = D::Target(epiphany_pipeline::PipelineTarget { revision: target.revision.wrapping_sub(1), ..target.clone() });
let predecessor = D::Target(eureka_pipeline::PipelineTarget { revision: target.revision.wrapping_sub(1), ..target.clone() });
revision_rule(docs, K::Target, key, target.revision, &predecessor)?;
unique_labels("target.invariants", target.invariants.iter().map(|invariant| invariant.label.0.as_str()))
}
@ -429,7 +429,7 @@ fn check(docs: &Docs, staged: &Staged, mind: &Slug) -> Result<(), MindRefusal> {
return Err(MindRefusal::UnknownDependency { cut: dependency.0.clone() });
}
}
let predecessor = D::CutSpec(epiphany_pipeline::PipelineCutSpec { revision: spec.revision.wrapping_sub(1), ..spec.clone() });
let predecessor = D::CutSpec(eureka_pipeline::PipelineCutSpec { revision: spec.revision.wrapping_sub(1), ..spec.clone() });
revision_rule(docs, K::CutSpec, key, spec.revision, &predecessor)
}
D::CutReport(report) => {
@ -704,7 +704,7 @@ mod tests {
use crate::receipt::{DocumentVersion, Faculty, HuginnCommitReceipt};
use crate::store::test_stores::{MemoryStore, RefusingStore, SwapCommand};
use cultcache_rs::DatabaseEntry;
use epiphany_pipeline::{PIPELINE_SCHEMA_EPOCH, PipelineDocument as D, PipelineKind as K};
use eureka_pipeline::{PIPELINE_SCHEMA_EPOCH, PipelineDocument as D, PipelineKind as K};
use sha2::{Digest, Sha256};
fn receipt_count<S: MindStore>(mind: &Mind<S>) -> usize {
@ -833,7 +833,7 @@ mod tests {
fn provenance_text_is_the_leafs_title_rule() {
let leaf_accepts = |text: &str| {
let D::Campaign(mut campaign) = campaign(&[REPO]) else { unreachable!() };
campaign.title = epiphany_pipeline::Title(text.into());
campaign.title = eureka_pipeline::Title(text.into());
D::Campaign(campaign).validate().is_ok()
};
let mine_accepts = |text: &str| {
@ -1280,7 +1280,7 @@ mod tests {
let mut steward = seeded();
committed(admit(&mut steward, vec![hand_off(INSTANCE, OTHER_INSTANCE, REPO, &[])]));
let D::HandOff(mut back) = hand_off(OTHER_INSTANCE, INSTANCE, REPO, &[]) else { panic!() };
back.handed_on = epiphany_pipeline::Date("2026-09-17".into());
back.handed_on = eureka_pipeline::Date("2026-09-17".into());
committed(admit(&mut steward, vec![D::HandOff(back)]));
assert_eq!(
refusal(admit(&mut steward, vec![stewardship_n(INSTANCE, REPO, 4)])),
@ -1366,7 +1366,7 @@ mod tests {
// Handed back, the mind stewards it again, as `n2`. No field names a
// return: this is the same hand-off shape with the instances swapped.
let D::HandOff(mut back) = hand_off(OTHER_INSTANCE, INSTANCE, REPO, &[]) else { panic!() };
back.handed_on = epiphany_pipeline::Date("2026-09-17".into());
back.handed_on = eureka_pipeline::Date("2026-09-17".into());
let (_, writes) = committed(admit(&mut mind, vec![D::HandOff(back)]));
let again = format!("{INSTANCE}:stewardship:gamecult_-epiphany.n2");
assert_eq!(writes, vec![
@ -1375,7 +1375,7 @@ mod tests {
]);
let Some(D::Stewardship(taken)) = mind.get(K::Stewardship, &again).unwrap() else { panic!() };
assert_eq!(taken.sequence, 2);
assert_eq!(taken.assigned_on, epiphany_pipeline::Date("2026-09-17".into()));
assert_eq!(taken.assigned_on, eureka_pipeline::Date("2026-09-17".into()));
assert!(mind.envelope(K::Stewardship, &first).is_some(), "the first assignment is still readable by key");
}
@ -1388,7 +1388,7 @@ mod tests {
let mut mind = seeded();
committed(admit(&mut mind, vec![hand_off(INSTANCE, OTHER_INSTANCE, REPO, &[])]));
let D::HandOff(mut back) = hand_off(OTHER_INSTANCE, INSTANCE, REPO, &[]) else { panic!() };
back.handed_on = epiphany_pipeline::Date("2026-09-17".into());
back.handed_on = eureka_pipeline::Date("2026-09-17".into());
committed(admit(&mut mind, vec![D::HandOff(back)]));
let first = format!("{INSTANCE}:stewardship:gamecult_-epiphany.n1");
let again = format!("{INSTANCE}:stewardship:gamecult_-epiphany.n2");
@ -1434,7 +1434,7 @@ mod tests {
let withdrawal = format!("{INSTANCE}:resolution:stewardship.gamecult_-epiphany.n1.n1");
committed(admit(&mut mind, vec![resolution(r(K::Resolution, &withdrawal), withdrawn())]));
let D::HandOff(mut again) = hand_off(INSTANCE, OTHER_INSTANCE, REPO, &[]) else { panic!() };
again.handed_on = epiphany_pipeline::Date("2026-09-17".into());
again.handed_on = eureka_pipeline::Date("2026-09-17".into());
let (_, writes) = committed(admit(&mut mind, vec![D::HandOff(again)]));
assert_eq!(writes, vec![
r(K::HandOff, &format!("{INSTANCE}:hand_off:{OTHER_INSTANCE}.gamecult_-epiphany.2026-09-17")),
@ -1444,7 +1444,7 @@ mod tests {
fn hand_off_on(from: &str, to: &str, day: &str) -> D {
let D::HandOff(mut hand_off) = hand_off(from, to, REPO, &[]) else { panic!() };
hand_off.handed_on = epiphany_pipeline::Date(day.into());
hand_off.handed_on = eureka_pipeline::Date(day.into());
D::HandOff(hand_off)
}
@ -1905,7 +1905,7 @@ mod tests {
report.repo = repo(OTHER_REPO);
assert_eq!(refusal(admit(&mut mind, vec![D::CutReport(report)])), MindRefusal::SpecMismatch { field: "repo".into() });
let mut report = cut_report("1", 1);
report.range.head = epiphany_pipeline::Sha("abcdef0".into());
report.range.head = eureka_pipeline::Sha("abcdef0".into());
assert_eq!(refusal(admit(&mut mind, vec![D::CutReport(report)])), MindRefusal::RangeOutsideCommits { head: "abcdef0".into() });
committed(admit(&mut mind, vec![resolution(r(K::CutSpec, &id("cut_spec", "cut-1.r1")), withdrawn())]));
assert_eq!(
@ -2008,18 +2008,18 @@ mod tests {
committed(admit(&mut mind, vec![D::CutSpec(cut_spec("1", 1))]));
// The commit is spelled short and the head full, and the reverse.
let mut report = cut_report("1", 1);
report.range.head = epiphany_pipeline::Sha(full.into());
report.range.head = eureka_pipeline::Sha(full.into());
committed(admit(&mut mind, vec![D::CutReport(report)]));
let mut report = cut_report("1", 2);
report.commits[0].sha = epiphany_pipeline::Sha(full.into());
report.commits[0].sha = eureka_pipeline::Sha(full.into());
committed(admit(&mut mind, vec![D::CutReport(report)]));
// A head that is no commit's prefix, long or short, is outside.
let mut report = cut_report("1", 3);
report.range.head = epiphany_pipeline::Sha("5f98229d0123456789abcdef0123456789abcdef".into());
report.range.head = eureka_pipeline::Sha("5f98229d0123456789abcdef0123456789abcdef".into());
assert!(matches!(refusal(admit(&mut mind, vec![D::CutReport(report)])), MindRefusal::RangeOutsideCommits { .. }));
let mut report = cut_report("1", 3);
report.commits[0].sha = epiphany_pipeline::Sha(full.into());
report.range.head = epiphany_pipeline::Sha("5f98229".into());
report.commits[0].sha = eureka_pipeline::Sha(full.into());
report.range.head = eureka_pipeline::Sha("5f98229".into());
assert!(matches!(refusal(admit(&mut mind, vec![D::CutReport(report)])), MindRefusal::RangeOutsideCommits { .. }));
}
@ -2115,16 +2115,16 @@ mod tests {
struct Rangeless {
campaign: Slug,
verdict: Short,
label: epiphany_pipeline::Label,
label: eureka_pipeline::Label,
confidence: FindingConfidence,
severity: epiphany_pipeline::FindingSeverity,
severity: eureka_pipeline::FindingSeverity,
claim: Line,
invariants: Vec<epiphany_pipeline::Label>,
locations: Vec<epiphany_pipeline::CodeLocation>,
failure_scenario: epiphany_pipeline::Para,
evidence: Vec<epiphany_pipeline::Evidence>,
precedents: Vec<epiphany_pipeline::ForeignRef>,
origin: epiphany_pipeline::FindingOrigin,
invariants: Vec<eureka_pipeline::Label>,
locations: Vec<eureka_pipeline::CodeLocation>,
failure_scenario: eureka_pipeline::Para,
evidence: Vec<eureka_pipeline::Evidence>,
precedents: Vec<eureka_pipeline::ForeignRef>,
origin: eureka_pipeline::FindingOrigin,
}
let whole = finding("1", 1, "F1", FindingConfidence::Plausible);
let rangeless = Rangeless {

View file

@ -6,7 +6,7 @@
//! `stored_at`.
use cultcache_rs::CultCacheEnvelope;
use epiphany_pipeline::{
use eureka_pipeline::{
OrgRepo, PipelineDocument, PipelineKind, PipelineRef, PipelineResolution, PipelineStewardship, ResolutionOutcome,
Short, Slug,
};

View file

@ -4,7 +4,7 @@
use chrono::{DateTime, TimeZone, Utc};
use cultcache_rs::CultCacheEnvelope;
use epiphany_pipeline::{
use eureka_pipeline::{
AuthorityMap, ClaimOutcome, CodeLocation, CommitRange, CutVerification, Date, DocRef, Evidence, EvidenceKind,
FindingConfidence, FindingOrigin, FindingSeverity, Label, Line, MutationRecord, OrgRepo, PipelineCampaign,
PipelineCutReport, PipelineCutSpec, PipelineDocument, PipelineFinding, PipelineFollowUp, PipelineHandOff,
@ -32,7 +32,7 @@ pub(crate) const NEAR_INSTANCE: &str = "yggdrasix";
pub(crate) const PREFIXED_INSTANCE: &str = "yggdrasil-two";
/// A foreign instance differing from `INSTANCE` in case alone. A `Slug` is
/// dot-joined labels, each `[A-Za-z0-9_-]{1,64}`, bounded to 64 bytes whole
/// (`epiphany_pipeline::dotted_text`), so this is a name a client may declare
/// (`eureka_pipeline::dotted_text`), so this is a name a client may declare
/// and a mind may hold: two minds whose names differ only in case are two
/// minds, and a comparison that folds case answers one of them for the other.
pub(crate) const CASED_INSTANCE: &str = "Yggdrasil";

View file

@ -7,7 +7,7 @@
//! how a change to it makes every projection rebuild. No network, no JSON, no
//! store handle: the daemon owns the vector store and the embedder.
use epiphany_pipeline::{PipelineDocument, PipelineKind, PipelineRef};
use eureka_pipeline::{PipelineDocument, PipelineKind, PipelineRef};
use sha2::{Digest, Sha256};
use crate::docs::Docs;
@ -154,7 +154,7 @@ mod tests {
use crate::fixtures::*;
use crate::receipt::Faculty;
use crate::store::test_stores::MemoryStore;
use epiphany_pipeline::{
use eureka_pipeline::{
ClaimOutcome, Deviation, FindingConfidence, PipelineDocument as D, PipelineKind as K, RulingAuthority,
};
use std::collections::BTreeMap;

View file

@ -18,7 +18,7 @@
//! so the daemon and the client share one set
//! of types.
//!
//! Document shape, bounds, formats and keys are `epiphany-pipeline`'s; the
//! Document shape, bounds, formats and keys are `eureka-pipeline`'s; the
//! organ registers, prepares, decodes and validates through the leaf's four
//! doors and never re-derives a key. The store is CultLib's owned redb
//! CultCache, whose lifetime-long exclusive lock is the single-writer
@ -59,4 +59,4 @@ pub use wire::{
/// The leaf, whole, through the one crate that pins its rev: the daemon and
/// the client name `Slug`, `PipelineRef` and the document types from here, so
/// one `[dependencies]` entry decides which revision the workspace speaks.
pub use epiphany_pipeline;
pub use eureka_pipeline;

View file

@ -13,7 +13,7 @@ use std::path::{Path, PathBuf};
use cultcache_rs::{CultCache, CultCacheEnvelope, DatabaseEntry, OwnedRedbMessagePackBackingStore};
use cultnet_rs::CursorKey;
use epiphany_pipeline::{PIPELINE_SCHEMA_EPOCH, PipelineDocument, PipelineKind, Slug, register_pipeline_document_types};
use eureka_pipeline::{PIPELINE_SCHEMA_EPOCH, PipelineDocument, PipelineKind, Slug, register_pipeline_document_types};
use crate::receipt::HuginnCommitReceipt;
use crate::refusal::MindRefusal;
@ -48,7 +48,7 @@ pub(crate) fn unavailable(error: anyhow::Error) -> MindRefusal {
}
/// A declared name's grammar, checked before its bytes are compared to
/// anything or reach the filesystem. `Slug` is `epiphany_pipeline`'s,
/// anything or reach the filesystem. `Slug` is `eureka_pipeline`'s,
/// dot-joined ASCII labels, and the leaf now opens a public door onto its own
/// check, `Slug::validate_slug`, on the pattern of `PipelineRef::validate_ref`
/// (Self's ruling on the F6 fork): the grammar is the leaf's, never
@ -64,7 +64,7 @@ pub(crate) fn unavailable(error: anyhow::Error) -> MindRefusal {
/// old wrapper's field, a client never sent) and not a label fragment.
fn require_grammatical_slug(field: &str, declared: &Slug) -> Result<(), MindRefusal> {
declared.validate_slug().map_err(|_| {
MindRefusal::Document(epiphany_pipeline::PipelineRefusal::InvalidFormat {
MindRefusal::Document(eureka_pipeline::PipelineRefusal::InvalidFormat {
field: field.into(),
value: declared.0.clone(),
})
@ -712,7 +712,7 @@ mod tests {
let escaping = Slug("..\\..\\escaped".into());
assert_eq!(
Mind::open(&inner, &escaping).err(),
Some(MindRefusal::Document(epiphany_pipeline::PipelineRefusal::InvalidFormat {
Some(MindRefusal::Document(eureka_pipeline::PipelineRefusal::InvalidFormat {
field: "instance".into(),
value: escaping.0.clone(),
}))
@ -734,7 +734,7 @@ mod tests {
let bad = Slug("not a slug!".into());
assert_eq!(
Mind::open_with(store.clone(), &bad).err(),
Some(MindRefusal::Document(epiphany_pipeline::PipelineRefusal::InvalidFormat {
Some(MindRefusal::Document(eureka_pipeline::PipelineRefusal::InvalidFormat {
field: "instance".into(),
value: bad.0.clone(),
}))
@ -753,7 +753,7 @@ mod tests {
let bad = Slug("not a slug!".into());
assert_eq!(
mind.require_instance(&bad).err(),
Some(MindRefusal::Document(epiphany_pipeline::PipelineRefusal::InvalidFormat {
Some(MindRefusal::Document(eureka_pipeline::PipelineRefusal::InvalidFormat {
field: "declared".into(),
value: bad.0.clone(),
}))
@ -773,7 +773,7 @@ mod tests {
assert!(!declared.0.is_ascii(), "U+2010 is not ASCII, unlike plain '-'");
assert_eq!(
Mind::open_with(MemoryStore::new(), &declared).err(),
Some(MindRefusal::Document(epiphany_pipeline::PipelineRefusal::InvalidFormat {
Some(MindRefusal::Document(eureka_pipeline::PipelineRefusal::InvalidFormat {
field: "instance".into(),
value: declared.0.clone(),
}))
@ -789,7 +789,7 @@ mod tests {
#[ignore = "reads and writes the scratch copy of a live state root named by HUGINN_MIND_SNAPSHOT"]
fn snapshot_admits_and_reads_back() {
use crate::fixtures::{admit, r, resolution, withdrawn};
use epiphany_pipeline::{PipelineRef, ResolutionOutcome, Short, pipeline_key};
use eureka_pipeline::{PipelineRef, ResolutionOutcome, Short, pipeline_key};
let root = std::env::var("HUGINN_MIND_SNAPSHOT").expect("HUGINN_MIND_SNAPSHOT names a scratch copy of the state root");
let mut mind = Mind::open(Path::new(&root), &slug("eureka")).unwrap();
@ -816,7 +816,7 @@ mod tests {
.unwrap_or_else(|| panic!("{} does not view", envelope.key));
assert_eq!(view.document, stored, "{}", envelope.key);
if let PipelineDocument::Resolution(held) = &stored
&& !matches!(held.outcome, epiphany_pipeline::ResolutionOutcome::Withdrawn { .. })
&& !matches!(held.outcome, eureka_pipeline::ResolutionOutcome::Withdrawn { .. })
&& !envelope.key.contains(":resolution:resolution.")
{
resolutions.push(envelope.key.clone());

View file

@ -25,7 +25,7 @@ use std::collections::BTreeMap;
use cultnet_rs::{
Cursor, EdgeAnchor, EdgeMatch, Evaluation, LIMIT_MAX, LIMIT_MIN, PROJECTION_DOCUMENT, Row, Selection, select, validate,
};
use epiphany_pipeline::{
use eureka_pipeline::{
ClaimOutcome, CommitRange, Date, FindingConfidence, FindingOrigin, FindingSeverity, Label, Line, OrgRepo,
PipelineDocument, PipelineKind, PipelineRef, PipelineResolution, ResolutionOutcome, RulingAuthority, Sha, Short,
Slug, Title,
@ -621,7 +621,7 @@ mod tests {
use crate::mind::schema_cache;
use crate::store::test_stores::MemoryStore;
use chrono::{TimeZone, Utc};
use epiphany_pipeline::{
use eureka_pipeline::{
ClaimOutcome, FindingConfidence, PipelineDocument as D, PipelineKind as K, PipelineRefusal, ResolutionOutcome,
};

View file

@ -16,7 +16,7 @@
use chrono::{DateTime, SecondsFormat, Utc};
use cultcache_rs::{CultCacheEnvelope, DatabaseEntry};
use epiphany_pipeline::{PipelineKind, PipelineRef, Short, Slug};
use eureka_pipeline::{PipelineKind, PipelineRef, Short, Slug};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha256};
@ -47,9 +47,9 @@ pub enum Faculty {
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
pub struct PipelineProvenance {
pub faculty: Faculty,
pub agent: epiphany_pipeline::Short,
pub session: epiphany_pipeline::Short,
pub tool: epiphany_pipeline::Short,
pub agent: eureka_pipeline::Short,
pub session: eureka_pipeline::Short,
pub tool: eureka_pipeline::Short,
}
/// The exact bytes of one stored document, as read or as written.
@ -311,7 +311,7 @@ mod tests {
use crate::mind::schema_cache;
use crate::store::test_stores::MemoryStore;
use cultcache_rs::{CacheBackingStore, PushAllOptions};
use epiphany_pipeline::{PipelineDocument, PipelineKind, PipelineRefusal};
use eureka_pipeline::{PipelineDocument, PipelineKind, PipelineRefusal};
/// A memory store that keeps the replacement set of every swap it was
/// asked for, so a test can count the commit's calls as well as read

View file

@ -3,7 +3,7 @@
//! serialises it, the tools (Cut 13) show it.
use cultnet_rs::SelectionRefusal;
use epiphany_pipeline::{PipelineKind, PipelineRefusal};
use eureka_pipeline::{PipelineKind, PipelineRefusal};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};

View file

@ -12,7 +12,7 @@
use std::collections::BTreeMap;
use cultnet_rs::{Row, RowSet, Selection};
use epiphany_pipeline::{OrgRepo, Label, PipelineDocument, PipelineKind, PipelineRef, ResolutionOutcome, Slug};
use eureka_pipeline::{OrgRepo, Label, PipelineDocument, PipelineKind, PipelineRef, ResolutionOutcome, Slug};
use crate::docs::{CitationRole, Held, kind_of_id, kind_of_type};
use crate::query::PipelineDocumentView;

View file

@ -6,7 +6,7 @@
use cultcache_rs::DatabaseEntry;
use cultnet_rs::{CultMeshCdnArtifactManifest, Selection};
use epiphany_pipeline::{PIPELINE_SCHEMA_EPOCH, PipelineKind, PipelineRef, Slug};
use eureka_pipeline::{PIPELINE_SCHEMA_EPOCH, PipelineKind, PipelineRef, Slug};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};

View file

@ -1,7 +1,7 @@
# Schemas
This folder is the canonical paperwork shrine for Epiphany's shared state
contracts.
This folder is the canonical paperwork shrine for the state contracts Huginn
publishes and the pipeline leaf derives.
If a Persona field or Mind document matters
enough to steer the machine, it should have a receipt here instead of living
@ -10,15 +10,13 @@ only in one Rust struct or one developer's damp recollection.
## Canonical surfaces
- [cultnet/gamecult.persona_state.v0.schema.json](./cultnet/gamecult.persona_state.v0.schema.json):
portable Persona state contract for Epiphany Persona, VoidBot repo Personas, and
portable Persona state contract for Eureka's Persona, VoidBot repo Personas, and
Ghostlight characters. It carries explicit provenance, public presentation
metadata, typed `candidateActions`, and a non-authoritative extension bag for
source-specific fields; social bonds, status reads, and doctrine stances are
typed affect records rather than generic thought blobs. Timestamps use JSON
Schema `date-time`, `presentation` is required, and `custom` enum values have
companion custom-label fields.
- [cultnet/epiphany.work_organ_state.v0.schema.json](./cultnet/epiphany.work_organ_state.v0.schema.json):
light function-shaped state for Epiphany internal work organs.
- [cultnet/README.md](./cultnet/README.md):
JSON Schema publication artifacts for typed CultNet boundaries. Live
providers own their schema-catalog responses.
@ -27,7 +25,7 @@ only in one Rust struct or one developer's damp recollection.
The living implementation is in code:
- [mind_documents.rs](/F:/Projects/Epiphany/epiphany-core/src/mind_documents.rs)
- [eureka-pipeline](../crates/eureka-pipeline/src/lib.rs): the pipeline document types and their derived schemas
The rule is simple:

View file

@ -9,3 +9,15 @@ type of the same name in `crates/huginn-mind/src/wire.rs`. Regenerate by hand
and the test refuses it:
`cargo test -p huginn-mind --lib -- --exact wire::tests::published_wire_schemas_match_derivation`
compares file to derivation byte for byte.
## The pipeline catalogue
`index.json` is the publication manifest for the thirteen
`epiphany.pipeline.*.v2` schemas and `gamecult.persona_state.v0`. The pipeline
schemas are derived from the value types in `crates/eureka-pipeline` and checked
byte for byte by its `pipeline_published_schemas_match_derivation` test. Each
document payload is `[value]`, a one-element MessagePack array whose element is
the named map the schema describes. Text bounds are UTF-8 bytes and enforced by
admission, not by the schema. A new named field with a serde default keeps
`epiphany.pipeline.epoch.v2`; a breaking change bumps the epoch, and the new
binary refuses the old store.