reply kind: one result of her Self session, as its actuator heard it (eureka-pipeline, huginn-mind, schemas, golden)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
196be4f11c
commit
3ef5da5e0d
21 changed files with 368 additions and 27 deletions
|
|
@ -29,6 +29,7 @@ sample.pulse yggdrasil:pulse:2026-10-31t041200-t0 epiphany.pipeline.pulse.v2 Epi
|
|||
sample.memory yggdrasil:memory:2026-10-31t041200-t0-m0 epiphany.pipeline.memory.v2 EpiphanyPipelineMemoryDocument 9189a8696e7374616e6365a979676764726173696ca56c6162656cb7323032362d31302d3331743034313230302d74302d6d30a4736f7274a9436f6d6d6974746564a474657874d936492074616b6520746865206261636b67726f756e6420736861646572733b204c6962627920686f6c64732074686520736f756e64732ea561626f757491ab406c696262793a63686174a66f726967696e81a4526f6f6d82a5726f6f6d73a5737761726daa68656172645f66726f6d91ab406c696262793a63686174a7736f757263657391a3246531b173616c69656e63655f7065726d696c6c65cd02bcac636f6e736f6c69646174657390
|
||||
sample.research eureka-state:research:p1.scope epiphany.pipeline.research.v2 EpiphanyPipelineResearchDocument 9189a863616d706169676eac657572656b612d7374617465a470617373a27031a56c6162656ca573636f7065a5636c61696dd93654686520656d626564206c696d6974206973203430393620746f6b656e7320776974682073696c656e74207472756e636174696f6e2ea664657461696cd934526561642066726f6d20746865206461656d6f6e277320696e646578206d6f64756c6520616e642070726f626564206c6976652ea8696e666572726564c2a7736f75726365739481a45265706f82a47265706fb147616d6543756c742f4570697068616e79a2617484a470617468d9256372617465732f687567696e6e2d6461656d6f6e2f7372632f696e6465782f6d6f642e7273aa73746172745f6c696e6532a8656e645f6c696e6538a6636f6d6d6974a8356639383232386481a357656283a375726cd92268747470733a2f2f7a657474656c6b617374656e2e64652f61746f6d69636974792fa4776f726bb65a657474656c6b617374656e2061746f6d6963697479ac7265747269657665645f6f6eaa323032362d30392d313581a84f6273657276656481a865766964656e636583a46b696e64a454657374a76c6f6361746f72aa636172676f2074657374a6726573756c74a26f6b81a8446f63756d656e7481a97265666572656e636582a46b696e64a652756c696e67a26964b6657572656b612d73746174653a72756c696e673a5238a67365727665739182a46b696e64a85175657374696f6ea26964b8657572656b612d73746174653a7175657374696f6e3a5131ab6f627365727665645f6f6eaa323032362d30392d3135
|
||||
sample.idea eureka-state:idea:p1.i1 epiphany.pipeline.idea.v2 EpiphanyPipelineIdeaDocument 9189a863616d706169676eac657572656b612d7374617465a470617373a27031a56c6162656ca26931a57469746c65b24f6e65206e6f74652070657220636c61696da469646561d9204b65657020612070617373206173206d616e7920736d616c6c206e6f7465732ea974726164656f666673b74d6f726520646f63756d656e747320746f206c6973742ea872657374735f6f6e9182a46b696e64a85265736561726368a26964be657572656b612d73746174653a72657365617263683a70312e73636f7065a6746f776172649182a46b696e64a85175657374696f6ea26964b8657572656b612d73746174653a7175657374696f6e3a5131ab70726f706f7365645f6f6eaa323032362d30392d3135
|
||||
sample.reply yggdrasil:reply:mind-2026-10-09-1-r1 epiphany.pipeline.reply.v2 EpiphanyPipelineReplyDocument 9185a8696e7374616e6365a979676764726173696ca56c6162656cb46d696e642d323032362d31302d30392d312d7231a372756e82a46b696e64a352756ea26964bf79676764726173696c3a72756e3a6d696e642d323032362d31302d30392d31a7616e73776572739182a46b696e64a8466f6c6c6f775570a26964d923657572656b612d73746174653a666f6c6c6f775f75703a61736b2d7468652d73656c66a474657874d93054686520736861646572207061737320697320646f6e653b2074686520736f756e647320617265204c6962627927732e
|
||||
resolution.superseded eureka-state:resolution:question.Q1.n1 epiphany.pipeline.resolution.v2 EpiphanyPipelineResolutionDocument 9185a77375626a65637482a46b696e64a85175657374696f6ea26964b8657572656b612d73746174653a7175657374696f6e3a5131a873657175656e636501a76f7574636f6d6581aa5375706572736564656481a262799282a46b696e64a652756c696e67a26964b6657572656b612d73746174653a72756c696e673a523882a46b696e64a85175657374696f6ea26964b8657572656b612d73746174653a7175657374696f6e3a5131a9726174696f6e616c65a852756c656420412eab7265736f6c7665645f6f6eaa323032362d30392d3135
|
||||
resolution.fixed-by-none eureka-state:resolution:question.Q1.n1 epiphany.pipeline.resolution.v2 EpiphanyPipelineResolutionDocument 9185a77375626a65637482a46b696e64a85175657374696f6ea26964b8657572656b612d73746174653a7175657374696f6e3a5131a873657175656e636501a76f7574636f6d6581a5466978656482a6636f6d6d6974a83566393832323864a26279c0a9726174696f6e616c65a852756c656420412eab7265736f6c7665645f6f6eaa323032362d30392d3135
|
||||
resolution.fixed-by-some eureka-state:resolution:question.Q1.n1 epiphany.pipeline.resolution.v2 EpiphanyPipelineResolutionDocument 9185a77375626a65637482a46b696e64a85175657374696f6ea26964b8657572656b612d73746174653a7175657374696f6e3a5131a873657175656e636501a76f7574636f6d6581a5466978656482a6636f6d6d6974a83566393832323864a2627982a46b696e64a94375745265706f7274a26964d921657572656b612d73746174653a6375745f7265706f72743a6375742d33612e6831a9726174696f6e616c65a852756c656420412eab7265736f6c7665645f6f6eaa323032362d30392d3135
|
||||
|
|
|
|||
|
|
@ -606,6 +606,22 @@ fn pulse_is_well_formed(pulse: &PipelinePulse, at: &str) -> Result<(), PipelineR
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// A reply says something and answers only follow-ups, from a run.
|
||||
fn reply_is_well_formed(reply: &PipelineReply, at: &str) -> Result<(), PipelineRefusal> {
|
||||
if reply.text.0.trim().is_empty() {
|
||||
return Err(format_error(&format!("{at}.text"), "a reply with no words"));
|
||||
}
|
||||
if reply.run.kind != PipelineKind::Run {
|
||||
return Err(format_error(&format!("{at}.run.kind"), reply.run.kind.name()));
|
||||
}
|
||||
for (index, answer) in reply.answers.iter().enumerate() {
|
||||
if answer.kind != PipelineKind::FollowUp {
|
||||
return Err(format_error(&format!("{at}.answers[{index}].kind"), answer.kind.name()));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// The most thousandths a memory's salience may carry.
|
||||
pub const SALIENCE_MAX_PERMILLE: u16 = 1000;
|
||||
|
||||
|
|
@ -820,6 +836,11 @@ value_types! {
|
|||
pub struct PipelinePulse {
|
||||
instance: Slug, label: Label, tempo_vote_beat_s: Option<u32>, tempo_vote_withdrawn: bool = absent, urgency_permille: Option<u16>, note: Line,
|
||||
} => pulse_is_well_formed
|
||||
/// One result of her Self session, as its actuator heard it: the words her
|
||||
/// Self ended a turn with, and the follow-ups (briefs) the turn answers.
|
||||
/// Instance-rooted: the key is `<instance>:reply:<label>`. Not semantically
|
||||
/// indexed. Whether the run is hers is admission's rule.
|
||||
pub struct PipelineReply { instance: Slug, label: Label, run: PipelineRef, answers: Vec<PipelineRef>[64], text: Para } => reply_is_well_formed
|
||||
/// One thing a Mind keeps for herself, in her own mind: what she was asked,
|
||||
/// decided, committed to, learned or did, an idea, or the rapport between
|
||||
/// her and another Mind or person. `text` is her own words. `about` names
|
||||
|
|
@ -1158,6 +1179,8 @@ pipeline_kinds! {
|
|||
"epiphany.pipeline.research.v2", "EpiphanyPipelineResearchDocument";
|
||||
Idea(PipelineIdea) => EpiphanyPipelineIdeaDocument, "idea",
|
||||
"epiphany.pipeline.idea.v2", "EpiphanyPipelineIdeaDocument";
|
||||
Reply(PipelineReply) => EpiphanyPipelineReplyDocument, "reply",
|
||||
"epiphany.pipeline.reply.v2", "EpiphanyPipelineReplyDocument";
|
||||
}
|
||||
|
||||
/// Parses a full document id: the grammar read backwards. A key and an id are
|
||||
|
|
@ -1354,6 +1377,7 @@ pub fn pipeline_key(document: &PipelineDocument) -> Result<String, PipelineRefus
|
|||
// A run hangs off its instance too: its label is its whole local.
|
||||
D::Run(value) => ("run.instance", value.instance.0.as_str(), local(&key_field, document_kind, &[&value.label.0])?),
|
||||
D::Pulse(value) => ("pulse.instance", value.instance.0.as_str(), local(&key_field, document_kind, &[&value.label.0])?),
|
||||
D::Reply(value) => ("reply.instance", value.instance.0.as_str(), local(&key_field, document_kind, &[&value.label.0])?),
|
||||
D::Memory(value) => ("memory.instance", value.instance.0.as_str(), local(&key_field, document_kind, &[&value.label.0])?),
|
||||
D::Research(value) => (campaign_field.as_str(), value.campaign.0.as_str(), local(&key_field, document_kind, &[&value.pass.0, &value.label.0])?),
|
||||
D::Idea(value) => (campaign_field.as_str(), value.campaign.0.as_str(), local(&key_field, document_kind, &[&value.pass.0, &value.label.0])?),
|
||||
|
|
@ -1628,6 +1652,12 @@ mod tests {
|
|||
rests_on: vec![PipelineRef { kind: PipelineKind::Research, id: id("research", "p1.scope") }],
|
||||
toward: vec![PipelineRef { kind: PipelineKind::Question, id: id("question", "Q1") }], proposed_on: date(),
|
||||
}), format!("{CAMPAIGN}:idea:p1.i1")),
|
||||
(D::Reply(PipelineReply {
|
||||
instance: slug(INSTANCE), label: l("mind-2026-10-09-1-r1"),
|
||||
run: PipelineRef { kind: PipelineKind::Run, id: Short(format!("{INSTANCE}:run:mind-2026-10-09-1")) },
|
||||
answers: vec![PipelineRef { kind: PipelineKind::FollowUp, id: id("follow_up", "ask-the-self") }],
|
||||
text: "The shader pass is done; the sounds are Libby's.".into(),
|
||||
}), format!("{INSTANCE}:reply:mind-2026-10-09-1-r1")),
|
||||
]
|
||||
}
|
||||
|
||||
|
|
@ -1941,6 +1971,27 @@ mod tests {
|
|||
}
|
||||
}
|
||||
|
||||
/// A reply has words, comes from a run, and answers only follow-ups.
|
||||
#[test]
|
||||
fn a_reply_has_words_a_run_and_follow_ups() {
|
||||
let validated = |change: &dyn Fn(&mut PipelineReply)| {
|
||||
let PipelineDocument::Reply(mut reply) = samples().remove(18).0 else { unreachable!() };
|
||||
change(&mut reply);
|
||||
PipelineDocument::Reply(reply).validate()
|
||||
};
|
||||
assert_eq!(validated(&|_| {}), Ok(()));
|
||||
assert_eq!(validated(&|reply| reply.answers.clear()), Ok(()), "a reply may answer nothing");
|
||||
for (change, field) in [
|
||||
(&(|reply: &mut PipelineReply| reply.text = "".into()) as &dyn Fn(&mut PipelineReply), "reply.text"),
|
||||
(&|reply: &mut PipelineReply| reply.text = "
|
||||
".into(), "reply.text"),
|
||||
(&|reply: &mut PipelineReply| reply.run = PipelineRef { kind: PipelineKind::FollowUp, id: id("follow_up", "ask-the-self") }, "reply.run.kind"),
|
||||
(&|reply: &mut PipelineReply| reply.answers[0] = PipelineRef { kind: PipelineKind::Run, id: Short(format!("{INSTANCE}:run:mind-2026-10-09-1")) }, "reply.answers[0].kind"),
|
||||
] {
|
||||
assert!(matches!(validated(change), Err(PipelineRefusal::InvalidFormat { field: f, .. }) if f == field), "{field}");
|
||||
}
|
||||
}
|
||||
|
||||
/// A pulse votes a tempo in bounds, declares an urgency in bounds, or does both; a pulse that does neither is refused.
|
||||
#[test]
|
||||
fn a_pulse_is_in_bounds_and_says_something() {
|
||||
|
|
@ -3640,11 +3691,11 @@ mod tests {
|
|||
};
|
||||
let document_schema = serde_json::to_value(schemars::schema_for!(PipelineDocument))?;
|
||||
let variants = document_schema["oneOf"].as_array().expect("the document schema is a oneOf");
|
||||
assert_eq!(PipelineKind::ALL.len(), 18, "eighteen kinds");
|
||||
assert_eq!(PipelineKind::ALL.len(), 19, "nineteen kinds");
|
||||
assert_eq!(
|
||||
variants.iter().map(tag).collect::<Vec<_>>(),
|
||||
PipelineKind::ALL.iter().map(|kind| kind.name().to_owned()).collect::<Vec<_>>(),
|
||||
"the document schema lists exactly the eighteen kinds, by name"
|
||||
"the document schema lists exactly the nineteen kinds, by name"
|
||||
);
|
||||
|
||||
let refusal_schema = serde_json::to_value(schemars::schema_for!(PipelineRefusal))?;
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ macro_rules! kinds {
|
|||
|
||||
kinds!(
|
||||
campaign, target, question, ruling, cut_spec, cut_report, verdict, finding, follow_up, resolution, instance, stewardship,
|
||||
hand_off, run, pulse, memory, research, idea,
|
||||
hand_off, run, pulse, memory, research, idea, reply,
|
||||
);
|
||||
|
||||
/// The schema named, or the names when none is: the exit code is 2 for a name
|
||||
|
|
|
|||
|
|
@ -313,6 +313,7 @@ fn refuse_foreign_instance(document: &PipelineDocument, mind: &Slug) -> Result<(
|
|||
PipelineDocument::Stewardship(value) if value.instance != *mind => Err(foreign(&value.instance)),
|
||||
PipelineDocument::Run(value) if value.instance != *mind => Err(foreign(&value.instance)),
|
||||
PipelineDocument::Pulse(value) if value.instance != *mind => Err(foreign(&value.instance)),
|
||||
PipelineDocument::Reply(value) if value.instance != *mind => Err(foreign(&value.instance)),
|
||||
PipelineDocument::Memory(value) if value.instance != *mind => Err(foreign(&value.instance)),
|
||||
PipelineDocument::HandOff(value) if value.from_instance != *mind && value.to_instance != *mind => {
|
||||
Err(foreign(&value.from_instance))
|
||||
|
|
@ -623,7 +624,7 @@ fn check(docs: &Docs<'_>, staged: &Staged, mind: &Slug) -> Result<(), MindRefusa
|
|||
let replay = docs.in_image(K::Run, key) == Some(&staged.document);
|
||||
run_rule(docs, key, run, replay)
|
||||
}
|
||||
D::FollowUp(_) | D::Instance(_) | D::Pulse(_) | D::Memory(_) | D::Research(_) | D::Idea(_) => Ok(()),
|
||||
D::FollowUp(_) | D::Instance(_) | D::Pulse(_) | D::Reply(_) | D::Memory(_) | D::Research(_) | D::Idea(_) => Ok(()),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2474,6 +2475,27 @@ mod tests {
|
|||
committed(admit(&mut mind, vec![pulse(INSTANCE)]));
|
||||
}
|
||||
|
||||
/// A reply is her own: one of another mind is a ForeignInstance, her own is admitted.
|
||||
#[test]
|
||||
fn a_reply_of_another_mind_is_a_foreign_instance_and_her_own_is_admitted() {
|
||||
let mut mind = with_specs();
|
||||
let reply = |instance: &str| {
|
||||
D::Reply(eureka_pipeline::PipelineReply {
|
||||
instance: slug(instance),
|
||||
label: l("w1-r1"),
|
||||
run: r(K::Run, &format!("{INSTANCE}:run:w1")),
|
||||
answers: vec![],
|
||||
text: "Done.".into(),
|
||||
})
|
||||
};
|
||||
assert_eq!(
|
||||
refusal(admit(&mut mind, vec![reply(OTHER_INSTANCE)])),
|
||||
MindRefusal::ForeignInstance { declared: OTHER_INSTANCE.into(), mind: INSTANCE.into() }
|
||||
);
|
||||
committed(admit(&mut mind, vec![run("w1", &[])]));
|
||||
committed(admit(&mut mind, vec![reply(INSTANCE)]));
|
||||
}
|
||||
|
||||
fn memory_key(label: &str) -> String {
|
||||
format!("{INSTANCE}:memory:{label}")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -343,7 +343,8 @@ pub(crate) fn line_of(document: &PipelineDocument) -> Option<Lineage<'_>> {
|
|||
| D::Instance(_)
|
||||
| D::HandOff(_)
|
||||
| D::Run(_)
|
||||
| D::Pulse(_) => None,
|
||||
| D::Pulse(_)
|
||||
| D::Reply(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -392,10 +393,11 @@ pub enum CitationRole {
|
|||
Sources,
|
||||
RestsOn,
|
||||
Toward,
|
||||
Run,
|
||||
}
|
||||
|
||||
impl CitationRole {
|
||||
pub const ALL: [CitationRole; 21] = [
|
||||
pub const ALL: [CitationRole; 22] = [
|
||||
Self::RaisedIn,
|
||||
Self::Answers,
|
||||
Self::Rulings,
|
||||
|
|
@ -417,6 +419,7 @@ impl CitationRole {
|
|||
Self::Sources,
|
||||
Self::RestsOn,
|
||||
Self::Toward,
|
||||
Self::Run,
|
||||
];
|
||||
|
||||
/// The wire spelling, equal to the referring field's name.
|
||||
|
|
@ -443,6 +446,7 @@ impl CitationRole {
|
|||
Self::Sources => "sources",
|
||||
Self::RestsOn => "rests_on",
|
||||
Self::Toward => "toward",
|
||||
Self::Run => "run",
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -467,6 +471,7 @@ impl CitationRole {
|
|||
Self::Consolidates => K::Memory,
|
||||
Self::Serves | Self::Sources => K::Research,
|
||||
Self::RestsOn | Self::Toward => K::Idea,
|
||||
Self::Run => K::Reply,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -484,6 +489,7 @@ impl CitationRole {
|
|||
Self::Verdict => vec![K::Verdict],
|
||||
Self::Claims => vec![K::CutSpec, K::Finding, K::FollowUp],
|
||||
Self::Consolidates => vec![K::Memory],
|
||||
Self::Run => vec![K::Run],
|
||||
Self::RaisedIn
|
||||
| Self::Source
|
||||
| Self::Subject
|
||||
|
|
@ -566,6 +572,7 @@ pub(crate) fn citations(document: &PipelineDocument) -> Vec<(CitationRole, Pipel
|
|||
hand_off.documents.iter().filter_map(|to| kind_of_id(&to.0).map(|kind| (R::Documents, cited(kind, to)))),
|
||||
),
|
||||
D::Run(run) => edges.extend(run.claims.iter().map(|to| (R::Claims, to.clone()))),
|
||||
D::Reply(reply) => edges.push((R::Run, reply.run.clone())),
|
||||
D::Memory(memory) => edges.extend(memory.consolidates.iter().map(|to| (R::Consolidates, to.clone()))),
|
||||
D::Research(research) => {
|
||||
edges.extend(research.serves.iter().map(|to| (R::Serves, to.clone())));
|
||||
|
|
|
|||
|
|
@ -101,7 +101,7 @@ pub fn index_text(document: &PipelineDocument) -> Option<String> {
|
|||
lines.push(&idea.idea.0);
|
||||
lines.push(&idea.tradeoffs.0);
|
||||
}
|
||||
D::Instance(_) | D::Stewardship(_) | D::Run(_) | D::Pulse(_) => {}
|
||||
D::Instance(_) | D::Stewardship(_) | D::Run(_) | D::Pulse(_) | D::Reply(_) => {}
|
||||
}
|
||||
lines.retain(|line| !line.is_empty());
|
||||
if lines.is_empty() {
|
||||
|
|
|
|||
|
|
@ -147,6 +147,7 @@ pub enum PipelineFacts {
|
|||
HandOff { from_instance: Slug, to_instance: Slug, repo: OrgRepo, handed_on: Date },
|
||||
Run { label: Label, operated_by: RunOperator, turn: RunTurn, started_on: Date, claims: Vec<PipelineRef> },
|
||||
Pulse { label: Label, tempo_vote_beat_s: Option<u32>, #[serde(default)] tempo_vote_withdrawn: bool, urgency_permille: Option<u16> },
|
||||
Reply { label: Label, run: PipelineRef, answers: Vec<PipelineRef> },
|
||||
Memory { label: Label, sort: MemorySort, about: Vec<Short>, origin: MemoryOrigin, salience_permille: u16 },
|
||||
Research { pass: Label, label: Label, claim: Line, inferred: bool, observed_on: Date },
|
||||
Idea { pass: Label, label: Label, title: Title, proposed_on: Date },
|
||||
|
|
@ -254,6 +255,7 @@ impl PipelineFacts {
|
|||
tempo_vote_withdrawn: value.tempo_vote_withdrawn,
|
||||
urgency_permille: value.urgency_permille,
|
||||
},
|
||||
D::Reply(value) => Self::Reply { label: value.label.clone(), run: value.run.clone(), answers: value.answers.clone() },
|
||||
D::Memory(value) => Self::Memory {
|
||||
label: value.label.clone(),
|
||||
sort: value.sort,
|
||||
|
|
@ -1736,6 +1738,10 @@ mod tests {
|
|||
let handoff = landed.iter().find(|write| write.kind == K::HandOff).expect("the hand-off landed").id.0.clone();
|
||||
let (_, landed) = committed(admit(&mut mind, vec![run("w1", &[r(K::CutSpec, &id("cut_spec", "cut-1.r2")), r(K::FollowUp, &fu)])]));
|
||||
let run_id = landed[0].id.0.clone();
|
||||
let reply_id = format!("{INSTANCE}:reply:w1-r1");
|
||||
committed(admit(&mut mind, vec![D::Reply(eureka_pipeline::PipelineReply {
|
||||
instance: INSTANCE.into(), label: "w1-r1".into(), run: r(K::Run, &run_id), answers: vec![r(K::FollowUp, &fu)], text: "Done.".into(),
|
||||
})]));
|
||||
let (older, newer) = (format!("{INSTANCE}:memory:m1"), format!("{INSTANCE}:memory:m2"));
|
||||
committed(admit(&mut mind, vec![memory("m1", MemorySort::Idea, "one", &[])]));
|
||||
let D::Memory(mut consolidating) = memory("m2", MemorySort::Idea, "two", &[]) else { unreachable!() };
|
||||
|
|
@ -1785,13 +1791,14 @@ mod tests {
|
|||
(CitationRole::Documents, &handoff, &q1),
|
||||
(CitationRole::Claims, &run_id, &spec_r2),
|
||||
(CitationRole::Claims, &run_id, &fu),
|
||||
(CitationRole::Run, &reply_id, &run_id),
|
||||
(CitationRole::Consolidates, &newer, &older),
|
||||
(CitationRole::Serves, ¬e, &q1),
|
||||
(CitationRole::Sources, ¬e, &f1),
|
||||
(CitationRole::RestsOn, &way, ¬e),
|
||||
(CitationRole::Toward, &way, &q1),
|
||||
];
|
||||
assert_eq!(CitationRole::ALL.len(), 21);
|
||||
assert_eq!(CitationRole::ALL.len(), 22);
|
||||
for role in CitationRole::ALL {
|
||||
let selection =
|
||||
Selection { cited: Some(Incoming { role: role.name().into(), exists: true }), ..Selection::default() };
|
||||
|
|
|
|||
|
|
@ -244,7 +244,7 @@ impl SelectionRow {
|
|||
D::FollowUp(follow_up) => vec![follow_up.repo.identity()],
|
||||
D::Stewardship(stewardship) => vec![stewardship.repo.identity()],
|
||||
D::HandOff(hand_off) => vec![hand_off.repo.identity()],
|
||||
D::Target(_) | D::Question(_) | D::Ruling(_) | D::Verdict(_) | D::Finding(_) | D::Instance(_) | D::Resolution(_) | D::Run(_) | D::Pulse(_) | D::Memory(_) | D::Research(_) | D::Idea(_) => {
|
||||
D::Target(_) | D::Question(_) | D::Ruling(_) | D::Verdict(_) | D::Finding(_) | D::Instance(_) | D::Resolution(_) | D::Run(_) | D::Pulse(_) | D::Reply(_) | D::Memory(_) | D::Research(_) | D::Idea(_) => {
|
||||
Vec::new()
|
||||
}
|
||||
},
|
||||
|
|
@ -329,7 +329,7 @@ impl Row for SelectionRow {
|
|||
}
|
||||
}
|
||||
|
||||
/// The organ's closed lists: eighteen schemas, twenty aliases, twenty-one roles.
|
||||
/// The organ's closed lists: nineteen schemas, twenty aliases, twenty-two roles.
|
||||
/// A schema's name is its id, so the substrate's alias matching resolves
|
||||
/// nothing beyond exact ids.
|
||||
pub(crate) struct Vocabulary;
|
||||
|
|
@ -375,7 +375,7 @@ fn invalid(field: impl Into<String>, value: &str, message: impl Into<String>) ->
|
|||
/// because `OrgRepo` identity is case-insensitive.
|
||||
///
|
||||
/// Refuses, naming the field the client sent: a `schemas` entry that is not
|
||||
/// one of the eighteen ids; a `keys` entry that is not a full id whose kind
|
||||
/// one of the nineteen ids; a `keys` entry that is not a full id whose kind
|
||||
/// segment reads and passes the leaf's ref grammar; an `any_of` value outside
|
||||
/// its alias's domain; and a `cites.target` whose key fails the ref grammar
|
||||
/// for its schema's kind (V24's rule, at this door). Whether the target
|
||||
|
|
@ -442,7 +442,7 @@ mod tests {
|
|||
/// refused at the substrate's door.
|
||||
#[test]
|
||||
fn each_kind_declares_exactly_its_aliases_and_none_is_numeric() {
|
||||
let table: [(K, &[&str]); 18] = [
|
||||
let table: [(K, &[&str]); 19] = [
|
||||
(K::Campaign, &["repo"]),
|
||||
(K::Target, &[]),
|
||||
(K::Question, &[]),
|
||||
|
|
@ -461,6 +461,7 @@ mod tests {
|
|||
(K::Memory, &["sort", "about", "memory_origin"]),
|
||||
(K::Research, &["pass"]),
|
||||
(K::Idea, &["pass"]),
|
||||
(K::Reply, &[]),
|
||||
];
|
||||
for (kind, extra) in table {
|
||||
let want = [&["root", "in_force", "faculty"][..], extra].concat();
|
||||
|
|
@ -473,10 +474,10 @@ mod tests {
|
|||
|
||||
/// Each role is declared by the one kind whose field carries it, and its
|
||||
/// targets are the kinds that field is typed to, "any" spelled as all
|
||||
/// eighteen.
|
||||
/// nineteen.
|
||||
#[test]
|
||||
fn each_kind_declares_the_roles_it_carries_and_their_targets() {
|
||||
let table: [(K, &[&str]); 18] = [
|
||||
let table: [(K, &[&str]); 19] = [
|
||||
(K::Campaign, &[]),
|
||||
(K::Target, &[]),
|
||||
(K::Question, &["raised_in"]),
|
||||
|
|
@ -495,13 +496,14 @@ mod tests {
|
|||
(K::Memory, &["consolidates"]),
|
||||
(K::Research, &["serves", "sources"]),
|
||||
(K::Idea, &["rests_on", "toward"]),
|
||||
(K::Reply, &["run"]),
|
||||
];
|
||||
for (kind, roles) in table {
|
||||
assert_eq!(sorted(Vocabulary.declared_roles(kind.type_id())), strings(roles), "{kind:?}");
|
||||
}
|
||||
assert_eq!(Vocabulary.target_leaves("answers"), vec![K::Question.type_id().to_string()]);
|
||||
assert_eq!(Vocabulary.target_leaves("rulings"), vec![K::Ruling.type_id().to_string()]);
|
||||
assert_eq!(Vocabulary.target_leaves("subject").len(), 18);
|
||||
assert_eq!(Vocabulary.target_leaves("subject").len(), 19);
|
||||
assert_eq!(
|
||||
sorted(Vocabulary.target_leaves("claims")),
|
||||
sorted([K::CutSpec, K::Finding, K::FollowUp].iter().map(|kind| kind.type_id().to_string()).collect())
|
||||
|
|
|
|||
|
|
@ -175,7 +175,8 @@
|
|||
"Pulse",
|
||||
"Memory",
|
||||
"Research",
|
||||
"Idea"
|
||||
"Idea",
|
||||
"Reply"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -69,7 +69,8 @@
|
|||
"Pulse",
|
||||
"Memory",
|
||||
"Research",
|
||||
"Idea"
|
||||
"Idea",
|
||||
"Reply"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -34,7 +34,8 @@
|
|||
"Pulse",
|
||||
"Memory",
|
||||
"Research",
|
||||
"Idea"
|
||||
"Idea",
|
||||
"Reply"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -130,7 +130,8 @@
|
|||
"Pulse",
|
||||
"Memory",
|
||||
"Research",
|
||||
"Idea"
|
||||
"Idea",
|
||||
"Reply"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -39,7 +39,8 @@
|
|||
"Pulse",
|
||||
"Memory",
|
||||
"Research",
|
||||
"Idea"
|
||||
"Idea",
|
||||
"Reply"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
|
|
|
|||
97
schemas/cultnet/epiphany.pipeline.reply.v2.schema.json
Normal file
97
schemas/cultnet/epiphany.pipeline.reply.v2.schema.json
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
{
|
||||
"$defs": {
|
||||
"Label": {
|
||||
"description": "A key label: `[A-Za-z0-9_-]{1,64}`.",
|
||||
"maxLength": 64,
|
||||
"type": "string"
|
||||
},
|
||||
"Para": {
|
||||
"description": "Text of at most 4,000 UTF-8 bytes; longer narrative is cited by `DocRef`.",
|
||||
"maxLength": 4000,
|
||||
"type": "string"
|
||||
},
|
||||
"PipelineKind": {
|
||||
"enum": [
|
||||
"Campaign",
|
||||
"Target",
|
||||
"Question",
|
||||
"Ruling",
|
||||
"CutSpec",
|
||||
"CutReport",
|
||||
"Verdict",
|
||||
"Finding",
|
||||
"FollowUp",
|
||||
"Resolution",
|
||||
"Instance",
|
||||
"Stewardship",
|
||||
"HandOff",
|
||||
"Run",
|
||||
"Pulse",
|
||||
"Memory",
|
||||
"Research",
|
||||
"Idea",
|
||||
"Reply"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"PipelineRef": {
|
||||
"description": "A reference to another document. The id is parsed as a full pipeline id of\nthe declared `kind`, so the kind and the id's kind segment cannot disagree.",
|
||||
"properties": {
|
||||
"id": {
|
||||
"$ref": "#/$defs/Short"
|
||||
},
|
||||
"kind": {
|
||||
"$ref": "#/$defs/PipelineKind"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"kind",
|
||||
"id"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"Short": {
|
||||
"description": "Text of at most 200 UTF-8 bytes.",
|
||||
"maxLength": 200,
|
||||
"type": "string"
|
||||
},
|
||||
"Slug": {
|
||||
"description": "A campaign slug or key segment: label parts joined by `.`.",
|
||||
"maxLength": 64,
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"$id": "https://gamecult.dev/epiphany/cultnet/epiphany.pipeline.reply.v2.schema.json",
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"description": "One result of her Self session, as its actuator heard it: the words her\nSelf ended a turn with, and the follow-ups (briefs) the turn answers.\nInstance-rooted: the key is `<instance>:reply:<label>`. Not semantically\nindexed. Whether the run is hers is admission's rule.",
|
||||
"properties": {
|
||||
"answers": {
|
||||
"items": {
|
||||
"$ref": "#/$defs/PipelineRef"
|
||||
},
|
||||
"maxItems": 64,
|
||||
"type": "array"
|
||||
},
|
||||
"instance": {
|
||||
"$ref": "#/$defs/Slug"
|
||||
},
|
||||
"label": {
|
||||
"$ref": "#/$defs/Label"
|
||||
},
|
||||
"run": {
|
||||
"$ref": "#/$defs/PipelineRef"
|
||||
},
|
||||
"text": {
|
||||
"$ref": "#/$defs/Para"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"instance",
|
||||
"label",
|
||||
"run",
|
||||
"answers",
|
||||
"text"
|
||||
],
|
||||
"title": "PipelineReply",
|
||||
"type": "object"
|
||||
}
|
||||
|
|
@ -101,7 +101,8 @@
|
|||
"Pulse",
|
||||
"Memory",
|
||||
"Research",
|
||||
"Idea"
|
||||
"Idea",
|
||||
"Reply"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -34,7 +34,8 @@
|
|||
"Pulse",
|
||||
"Memory",
|
||||
"Research",
|
||||
"Idea"
|
||||
"Idea",
|
||||
"Reply"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -72,7 +72,8 @@
|
|||
"Pulse",
|
||||
"Memory",
|
||||
"Research",
|
||||
"Idea"
|
||||
"Idea",
|
||||
"Reply"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -29,7 +29,8 @@
|
|||
"Pulse",
|
||||
"Memory",
|
||||
"Research",
|
||||
"Idea"
|
||||
"Idea",
|
||||
"Reply"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1310,6 +1310,22 @@
|
|||
"kind",
|
||||
"value"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"kind": {
|
||||
"type": "string",
|
||||
"const": "reply"
|
||||
},
|
||||
"value": {
|
||||
"$ref": "#/$defs/PipelineReply"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"kind",
|
||||
"value"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
@ -1563,7 +1579,8 @@
|
|||
"Pulse",
|
||||
"Memory",
|
||||
"Research",
|
||||
"Idea"
|
||||
"Idea",
|
||||
"Reply"
|
||||
]
|
||||
},
|
||||
"PipelineMemory": {
|
||||
|
|
@ -1762,6 +1779,38 @@
|
|||
"id"
|
||||
]
|
||||
},
|
||||
"PipelineReply": {
|
||||
"description": "One result of her Self session, as its actuator heard it: the words her\nSelf ended a turn with, and the follow-ups (briefs) the turn answers.\nInstance-rooted: the key is `<instance>:reply:<label>`. Not semantically\nindexed. Whether the run is hers is admission's rule.",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"answers": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/$defs/PipelineRef"
|
||||
},
|
||||
"maxItems": 64
|
||||
},
|
||||
"instance": {
|
||||
"$ref": "#/$defs/Slug"
|
||||
},
|
||||
"label": {
|
||||
"$ref": "#/$defs/Label"
|
||||
},
|
||||
"run": {
|
||||
"$ref": "#/$defs/PipelineRef"
|
||||
},
|
||||
"text": {
|
||||
"$ref": "#/$defs/Para"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"instance",
|
||||
"label",
|
||||
"run",
|
||||
"answers",
|
||||
"text"
|
||||
]
|
||||
},
|
||||
"PipelineResearch": {
|
||||
"description": "One claim a pass found, with the sources it stands on, grouped by `pass`.\nWhat is so, never what to do: a recommendation is an idea. `inferred`\nmarks a claim read from its sources rather than seen in them. It stores\nno status: whether a note is current, superseded or wrong is its\nresolutions'. `serves` names the question, cut or campaign document it\nwas gathered for. The pass's method and what it did not search is its\nfirst note, labelled `scope`. Campaign-rooted:\n`<campaign>:research:<pass>.<label>`.",
|
||||
"type": "object",
|
||||
|
|
|
|||
|
|
@ -260,7 +260,8 @@
|
|||
"serves",
|
||||
"sources",
|
||||
"rests_on",
|
||||
"toward"
|
||||
"toward",
|
||||
"run"
|
||||
]
|
||||
},
|
||||
"ClaimOutcome": {
|
||||
|
|
@ -2612,6 +2613,22 @@
|
|||
"kind",
|
||||
"value"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"kind": {
|
||||
"type": "string",
|
||||
"const": "reply"
|
||||
},
|
||||
"value": {
|
||||
"$ref": "#/$defs/PipelineReply"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"kind",
|
||||
"value"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
@ -3330,6 +3347,41 @@
|
|||
"value"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"kind": {
|
||||
"type": "string",
|
||||
"const": "reply"
|
||||
},
|
||||
"value": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"answers": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/$defs/PipelineRef"
|
||||
}
|
||||
},
|
||||
"label": {
|
||||
"$ref": "#/$defs/Label"
|
||||
},
|
||||
"run": {
|
||||
"$ref": "#/$defs/PipelineRef"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"label",
|
||||
"run",
|
||||
"answers"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"kind",
|
||||
"value"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
|
@ -3704,7 +3756,8 @@
|
|||
"Pulse",
|
||||
"Memory",
|
||||
"Research",
|
||||
"Idea"
|
||||
"Idea",
|
||||
"Reply"
|
||||
]
|
||||
},
|
||||
"PipelineMemory": {
|
||||
|
|
@ -4071,6 +4124,38 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
"PipelineReply": {
|
||||
"description": "One result of her Self session, as its actuator heard it: the words her\nSelf ended a turn with, and the follow-ups (briefs) the turn answers.\nInstance-rooted: the key is `<instance>:reply:<label>`. Not semantically\nindexed. Whether the run is hers is admission's rule.",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"answers": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/$defs/PipelineRef"
|
||||
},
|
||||
"maxItems": 64
|
||||
},
|
||||
"instance": {
|
||||
"$ref": "#/$defs/Slug"
|
||||
},
|
||||
"label": {
|
||||
"$ref": "#/$defs/Label"
|
||||
},
|
||||
"run": {
|
||||
"$ref": "#/$defs/PipelineRef"
|
||||
},
|
||||
"text": {
|
||||
"$ref": "#/$defs/Para"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"instance",
|
||||
"label",
|
||||
"run",
|
||||
"answers",
|
||||
"text"
|
||||
]
|
||||
},
|
||||
"PipelineResearch": {
|
||||
"description": "One claim a pass found, with the sources it stands on, grouped by `pass`.\nWhat is so, never what to do: a recommendation is an idea. `inferred`\nmarks a claim read from its sources rather than seen in them. It stores\nno status: whether a note is current, superseded or wrong is its\nresolutions'. `serves` names the question, cut or campaign document it\nwas gathered for. The pass's method and what it did not search is its\nfirst note, labelled `scope`. Campaign-rooted:\n`<campaign>:research:<pass>.<label>`.",
|
||||
"type": "object",
|
||||
|
|
|
|||
|
|
@ -210,6 +210,17 @@
|
|||
"title": "Epiphany Pipeline Idea v2",
|
||||
"path": "epiphany.pipeline.idea.v2.schema.json"
|
||||
},
|
||||
{
|
||||
"schemaId": "https://gamecult.dev/epiphany/cultnet/epiphany.pipeline.reply.v2.schema.json",
|
||||
"kind": "document_payload",
|
||||
"wireContracts": [
|
||||
"cultnet.schema.v0"
|
||||
],
|
||||
"schemaVersion": "epiphany.pipeline.reply.v2",
|
||||
"documentType": "epiphany.pipeline.reply.v2",
|
||||
"title": "Epiphany Pipeline Reply v2",
|
||||
"path": "epiphany.pipeline.reply.v2.schema.json"
|
||||
},
|
||||
{
|
||||
"schemaId": "huginn.mind_request.v1",
|
||||
"kind": "wire_message",
|
||||
|
|
|
|||
Loading…
Reference in a new issue