eureka-pipeline: a resolution outcome Moved { to } closes a campaign for the mind that gives it up

Additive resolution v2 variant, bounded as a Slug; the golden gains one line, the published schema one arm. Admission owns when it may be written.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Meta Vens 2026-11-08 17:47:32 +01:00
commit f29e15481e
3 changed files with 39 additions and 0 deletions

View file

@ -53,3 +53,4 @@ cut_spec.reads-and-symbol eureka-state:cut_spec:cut-3a.r1 epiphany.pipeline.cut_
finding.location-symbol eureka-state:finding:cut-3a.s2.F4 epiphany.pipeline.finding.v2 EpiphanyPipelineFindingDocument 918da863616d706169676eac657572656b612d7374617465a776657264696374be657572656b612d73746174653a766572646963743a6375742d33612e7332a56c6162656ca24634a572616e676582a462617365a83566393832323864a468656164a83566393832323864aa636f6e666964656e6365a9436f6e6669726d6564a87365766572697479a448696768a5636c61696dd9214120646f74746564206c6162656c20636f6d706f7365732074776f206b6579732eaa696e76617269616e747391ab6d696e642d61646d697473a96c6f636174696f6e739184a470617468d9216372617465732f657572656b612d706970656c696e652f7372632f6c69622e7273a46c696e6501a8656e645f6c696e6509a673796d626f6cab2631313533303930343938b06661696c7572655f7363656e6172696fbc54776f20646f63756d656e747320636c61696d206f6e65206b65792ea865766964656e63659183a46b696e64a454657374a76c6f6361746f72aa636172676f2074657374a6726573756c74a26f6baa707265636564656e747390a66f726967696eaa496e74726f6475636564
target.repos eureka-state:target:r2 epiphany.pipeline.target.v2 EpiphanyPipelineTargetDocument 9187a863616d706169676eac657572656b612d7374617465a87265766973696f6e02aa696e76617269616e74739182a56c6162656cab6d696e642d61646d697473a973746174656d656e74b64f6e6c792061646d697373696f6e207772697465732eac6e6f745f696e5f73636f706591ac4576652062726f7773696e67b963616e6f6e6963616c5f696d706c656d656e746174696f6e7391a743756c744c6962a57265706f7392b147616d6543756c742f4570697068616e79af47616d6543756c742f487567696e6ea3646f6384a470617468d9256e6f7465732f657572656b612d706970656c696e652d73746174652d7461726765742e6d64aa73746172745f6c696e6501a8656e645f6c696e6509a6636f6d6d6974a83566393832323864
stewardship.names yggdrasil:stewardship:gamecult_-epiphany.n1 epiphany.pipeline.stewardship.v2 EpiphanyPipelineStewardshipDocument 9186a8696e7374616e6365a979676764726173696ca47265706fb147616d6543756c742f4570697068616e79a873657175656e636501ab61737369676e65645f6f6eaa323032362d30392d3135a46e6f7465b954686520457572656b612063616d706169676e207265706f2ea56e616d657392a3424453b1426164204461792053696d756c61746f72
resolution.moved eureka-state:resolution:question.Q1.n1 epiphany.pipeline.resolution.v2 EpiphanyPipelineResolutionDocument 9185a77375626a65637482a46b696e64a85175657374696f6ea26964b8657572656b612d73746174653a7175657374696f6e3a5131a873657175656e636501a76f7574636f6d6581a54d6f76656481a2746fa86865696d64616c6ca9726174696f6e616c65a852756c656420412eab7265736f6c7665645f6f6eaa323032362d30392d3135

View file

@ -1099,6 +1099,9 @@ pub enum ResolutionOutcome {
/// How a Sleep run ended when the ledger took her reply: `line` is her one line, `day_through` the last memory
/// of her day she was shown (none when the day was empty). It is the one place a night's cut line is kept.
Slept { line: Line, day_through: Option<PipelineRef> },
/// A campaign's history left this mind for `to`, the mind that now keeps it. Final: the campaign and every
/// document under its root read resolved by this and take no further writes here.
Moved { to: Slug },
}
impl Bounded for ResolutionOutcome {
@ -1116,6 +1119,7 @@ impl Bounded for ResolutionOutcome {
line.validate(&format!("{field}.line"))?;
day_through.validate(&format!("{field}.day_through"))
}
Self::Moved { to } => to.validate(&format!("{field}.to")),
}
}
}
@ -1948,6 +1952,7 @@ mod tests {
let mut stewardship = stewardship_sample();
stewardship.names = vec![s("BDS"), s("Bad Day Simulator")];
out.push(("stewardship.names".into(), D::Stewardship(stewardship)));
out.push(("resolution.moved".into(), resolution(ResolutionOutcome::Moved { to: Slug("heimdall".into()) })));
out
}
@ -3087,6 +3092,13 @@ mod tests {
assert_eq!(slept("ok".into(), None), Ok(()));
assert_eq!(slept(Line("a".repeat(1001)), None), Err(PipelineRefusal::FieldBound { field: "resolution.outcome.line".into(), limit: 1000, actual: 1001 }));
refused("resolution.outcome.day_through.id", slept("ok".into(), Some(PipelineRef { kind: PipelineKind::Memory, id: id("ruling", "R8") })));
// A move names the receiving mind as any other slug is named, and refuses one that is not a slug.
let campaign = PipelineRef { kind: PipelineKind::Campaign, id: id("campaign", "self") };
let moved = |to: &str| resolved(campaign.clone(), ResolutionOutcome::Moved { to: Slug(to.into()) });
assert_eq!(moved("heimdall"), Ok(()));
refused("resolution.outcome.to", moved("Not A Slug!"));
refused("resolution.outcome.to", moved(""));
}
/// Ruling B: a fix names the tree where the finding stopped being true,

View file

@ -224,6 +224,27 @@
"Slept"
],
"type": "object"
},
{
"additionalProperties": false,
"description": "A campaign's history left this mind for `to`, the mind that now keeps it. Final: the campaign and every\ndocument under its root read resolved by this and take no further writes here.",
"properties": {
"Moved": {
"properties": {
"to": {
"$ref": "#/$defs/Slug"
}
},
"required": [
"to"
],
"type": "object"
}
},
"required": [
"Moved"
],
"type": "object"
}
]
},
@ -236,6 +257,11 @@
"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.resolution.v2.schema.json",