Bound fission input before inference
This commit is contained in:
parent
07176cdc2e
commit
debdf609d3
5 changed files with 63 additions and 26 deletions
|
|
@ -555,28 +555,11 @@ impl WorldCompiler {
|
|||
Vec<VaultEvidenceReceipt>,
|
||||
Vec<ModelStageReceipt>,
|
||||
)> {
|
||||
let requested = validate_fission_request(&request)?;
|
||||
let parent = campaign
|
||||
.gestalts
|
||||
.get(&request.parent_gestalt_id)
|
||||
.ok_or_else(|| anyhow!("fission parent is unknown"))?;
|
||||
let requested: BTreeSet<_> = request
|
||||
.requested_partition_values
|
||||
.iter()
|
||||
.map(|value| value.trim().to_ascii_lowercase())
|
||||
.collect();
|
||||
if request.reason.trim().is_empty()
|
||||
|| requested.is_empty()
|
||||
|| requested.len() != request.requested_partition_values.len()
|
||||
|| requested.contains("other/unknown")
|
||||
|| request
|
||||
.requested_partition_values
|
||||
.iter()
|
||||
.any(|value| value.trim().is_empty())
|
||||
{
|
||||
return Err(anyhow!(
|
||||
"fission request needs distinct named cuts and a reason"
|
||||
));
|
||||
}
|
||||
let subject = serde_json::json!({
|
||||
"request":request,
|
||||
"parent":parent,
|
||||
|
|
@ -1217,6 +1200,31 @@ fn validate_user_text(label: &str, value: &str, max_chars: usize) -> Result<()>
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn validate_fission_request(request: &GestaltFissionRequest) -> Result<BTreeSet<String>> {
|
||||
validate_user_text("fission reason", &request.reason, 500)?;
|
||||
if request.requested_partition_values.is_empty()
|
||||
|| request.requested_partition_values.len() > 16
|
||||
{
|
||||
return Err(anyhow!("fission request needs between 1 and 16 named cuts"));
|
||||
}
|
||||
for value in &request.requested_partition_values {
|
||||
validate_user_text("fission cut", value, 160)?;
|
||||
}
|
||||
let requested: BTreeSet<_> = request
|
||||
.requested_partition_values
|
||||
.iter()
|
||||
.map(|value| value.trim().to_ascii_lowercase())
|
||||
.collect();
|
||||
if requested.len() != request.requested_partition_values.len()
|
||||
|| requested.contains("other/unknown")
|
||||
{
|
||||
return Err(anyhow!(
|
||||
"fission request needs distinct named cuts and reserves other/unknown for the compiler"
|
||||
));
|
||||
}
|
||||
Ok(requested)
|
||||
}
|
||||
|
||||
fn global_agency_queries(start: &CustomStart) -> Vec<String> {
|
||||
let horizon = start.when.chars().take(120).collect::<String>();
|
||||
vec![
|
||||
|
|
@ -2295,6 +2303,29 @@ mod tests {
|
|||
assert!(validate_user_text("field", "hello\0world", 20).is_err());
|
||||
assert!(validate_user_text("field", "hello\nworld", 20).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fission_text_is_bounded_before_retrieval_or_inference() {
|
||||
let mut request = GestaltFissionRequest {
|
||||
parent_gestalt_id: "population".into(),
|
||||
partition_axis: AgencyAxis::Geography,
|
||||
requested_partition_values: vec!["harbor".into(), "inland".into()],
|
||||
reason: "The population is dispersing along established routes.".into(),
|
||||
};
|
||||
assert!(validate_fission_request(&request).is_ok());
|
||||
|
||||
request.requested_partition_values = (0..17).map(|index| format!("cut-{index}")).collect();
|
||||
assert!(validate_fission_request(&request).is_err());
|
||||
request.requested_partition_values = vec!["x".repeat(161)];
|
||||
assert!(validate_fission_request(&request).is_err());
|
||||
request.requested_partition_values = vec!["Harbor".into(), "harbor".into()];
|
||||
assert!(validate_fission_request(&request).is_err());
|
||||
request.requested_partition_values = vec!["other/unknown".into()];
|
||||
assert!(validate_fission_request(&request).is_err());
|
||||
request.requested_partition_values = vec!["harbor".into()];
|
||||
request.reason = "x".repeat(501);
|
||||
assert!(validate_fission_request(&request).is_err());
|
||||
}
|
||||
use crate::{domain::SourceWitness, model::ModelPort, vault::FixtureVault};
|
||||
use async_trait::async_trait;
|
||||
use sha2::Digest;
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
6
web/dist/index.html
vendored
6
web/dist/index.html
vendored
|
|
@ -1,5 +1,5 @@
|
|||
<!doctype html>
|
||||
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>GhostlightDungeon</title> <script type="module" crossorigin src="/assets/index-CA3oKXnL.js"></script>
|
||||
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>GhostlightDungeon</title> <script type="module" crossorigin src="/assets/index-CGgopXvh.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-B7Qh0xAi.css">
|
||||
</head>
|
||||
<body><header><p class="eyebrow">STARFIRE / PRIVATE VERSE</p><h1>GhostlightDungeon</h1><p id="status" role="status" aria-live="polite">Reading the world…</p></header><main>
|
||||
|
|
@ -7,10 +7,10 @@
|
|||
<form id="suggest-form"><label for="setting">Generate three grounded openings</label><input id="setting" value="Aetheria" maxlength="120" required><button type="submit">Retrieve and suggest</button></form>
|
||||
<form id="custom-form"><p class="form-heading">Or describe your own beginning</p><label for="campaign-name">Campaign name</label><input id="campaign-name" name="campaign_name" maxlength="80" required><label for="custom-who">Who are you?</label><input id="custom-who" name="who" maxlength="500" required><label for="custom-where">Where are you?</label><input id="custom-where" name="where_" maxlength="500" required><label for="custom-when">When?</label><input id="custom-when" name="when" maxlength="500" required><label for="custom-goal">What do you want?</label><textarea id="custom-goal" name="goal" maxlength="1000" required></textarea><button type="submit">Compile custom start</button></form>
|
||||
</div><div id="compiler-results"></div></section><section id="surface"></section>
|
||||
<form id="composer"><label for="attempt">What does your character do or say?</label><textarea id="attempt" name="attempt" required></textarea><label for="intended-effect">What outcome are they trying to cause? <span class="quiet">Required for an assessed attempt</span></label><input id="intended-effect" name="intended_effect"><p class="quiet">Use Speak when the words themselves are the action. Use Assess attempt when you want the words or action to persuade, deceive, intimidate, overcome resistance, or cause another uncertain effect.</p><div><button type="button" id="speak">Speak</button><button type="submit">Assess attempt</button><button type="button" id="wait">Wait one hour</button></div></form><section id="receipt" class="card" hidden aria-live="polite"></section>
|
||||
<form id="composer"><label for="attempt">What does your character do or say?</label><textarea id="attempt" name="attempt" maxlength="4000" required></textarea><label for="intended-effect">What outcome are they trying to cause? <span class="quiet">Required for an assessed attempt</span></label><input id="intended-effect" name="intended_effect" maxlength="1000"><p class="quiet">Use Speak when the words themselves are the action. Use Assess attempt when you want the words or action to persuade, deceive, intimidate, overcome resistance, or cause another uncertain effect.</p><div><button type="button" id="speak">Speak</button><button type="submit">Assess attempt</button><button type="button" id="wait">Wait one hour</button></div></form><section id="receipt" class="card" hidden aria-live="polite"></section>
|
||||
<form id="destination-form"><p class="form-heading">Compile a destination without moving there</p><label for="destination-origin">Current location id</label><input id="destination-origin" name="origin_location_id" readonly required><label for="destination-request">Where do you want the world to extend?</label><input id="destination-request" name="destination" maxlength="500" required><button type="submit">Retrieve destination</button></form>
|
||||
<section id="campaign-lab" hidden><h2>Campaign laboratory</h2><div id="campaign-list"></div>
|
||||
<form id="resolution-form"><label for="active-cell-budget">Active Persona-cell budget: <output id="active-cell-budget-value">8</output></label><input id="active-cell-budget" name="active_cell_budget" type="range" min="1" max="32" value="8"><p id="resolution-status"></p><button type="submit">Apply at safe boundary</button></form>
|
||||
<form id="fission-form"><p class="form-heading">Approval-gated population fission</p><label for="fission-parent">Population</label><select name="parent_gestalt_id" id="fission-parent" required></select><label for="fission-axis">Partition axis</label><select id="fission-axis" name="partition_axis"><option value="geography">Geography</option><option value="ideology">Ideology</option><option value="authority">Authority</option><option value="economy_role">Economic role</option><option value="species_body">Species/body</option><option value="information">Information</option></select><label for="fission-values">Named cuts, comma separated</label><input id="fission-values" name="requested_partition_values" required><label for="fission-reason">Why does this population need canonical leaves?</label><input id="fission-reason" name="reason" required><button type="submit">Compile fission preview</button></form>
|
||||
<form id="fission-form"><p class="form-heading">Approval-gated population fission</p><label for="fission-parent">Population</label><select name="parent_gestalt_id" id="fission-parent" required></select><label for="fission-axis">Partition axis</label><select id="fission-axis" name="partition_axis"><option value="geography">Geography</option><option value="ideology">Ideology</option><option value="authority">Authority</option><option value="economy_role">Economic role</option><option value="species_body">Species/body</option><option value="information">Information</option></select><label for="fission-values">Named cuts, comma separated (up to 16)</label><input id="fission-values" name="requested_partition_values" maxlength="2575" required><label for="fission-reason">Why does this population need canonical leaves?</label><input id="fission-reason" name="reason" maxlength="500" required><button type="submit">Compile fission preview</button></form>
|
||||
<form id="fork-form"><label for="fork-name">Fork name</label><input id="fork-name" name="name" maxlength="80" required><button type="submit">Fork selected campaign</button></form><form id="reset-form"><label for="reset-name">Reset branch name</label><input id="reset-name" name="name" maxlength="80" required><button type="submit">Reset from selected branch seed</button></form><a href="/api/campaigns/export" download>Export selected CultCache .cc</a> <a href="/api/campaigns/canon-candidates.md" download>Export canon candidates Markdown</a><details id="operator-lab" hidden><summary>Operator inspector</summary><form id="provider-parallelism-form"><label for="provider-parallelism">Provider request parallelism: <output id="provider-parallelism-value">8</output></label><input id="provider-parallelism" type="range" min="1" max="32" value="8"><button type="submit">Apply operator limit</button></form><button type="button" id="load-operator">Load private state and receipts</button><div id="operator-output"></div></details></section>
|
||||
</main></body></html>
|
||||
|
|
|
|||
|
|
@ -5,10 +5,10 @@
|
|||
<form id="suggest-form"><label for="setting">Generate three grounded openings</label><input id="setting" value="Aetheria" maxlength="120" required><button type="submit">Retrieve and suggest</button></form>
|
||||
<form id="custom-form"><p class="form-heading">Or describe your own beginning</p><label for="campaign-name">Campaign name</label><input id="campaign-name" name="campaign_name" maxlength="80" required><label for="custom-who">Who are you?</label><input id="custom-who" name="who" maxlength="500" required><label for="custom-where">Where are you?</label><input id="custom-where" name="where_" maxlength="500" required><label for="custom-when">When?</label><input id="custom-when" name="when" maxlength="500" required><label for="custom-goal">What do you want?</label><textarea id="custom-goal" name="goal" maxlength="1000" required></textarea><button type="submit">Compile custom start</button></form>
|
||||
</div><div id="compiler-results"></div></section><section id="surface"></section>
|
||||
<form id="composer"><label for="attempt">What does your character do or say?</label><textarea id="attempt" name="attempt" required></textarea><label for="intended-effect">What outcome are they trying to cause? <span class="quiet">Required for an assessed attempt</span></label><input id="intended-effect" name="intended_effect"><p class="quiet">Use Speak when the words themselves are the action. Use Assess attempt when you want the words or action to persuade, deceive, intimidate, overcome resistance, or cause another uncertain effect.</p><div><button type="button" id="speak">Speak</button><button type="submit">Assess attempt</button><button type="button" id="wait">Wait one hour</button></div></form><section id="receipt" class="card" hidden aria-live="polite"></section>
|
||||
<form id="composer"><label for="attempt">What does your character do or say?</label><textarea id="attempt" name="attempt" maxlength="4000" required></textarea><label for="intended-effect">What outcome are they trying to cause? <span class="quiet">Required for an assessed attempt</span></label><input id="intended-effect" name="intended_effect" maxlength="1000"><p class="quiet">Use Speak when the words themselves are the action. Use Assess attempt when you want the words or action to persuade, deceive, intimidate, overcome resistance, or cause another uncertain effect.</p><div><button type="button" id="speak">Speak</button><button type="submit">Assess attempt</button><button type="button" id="wait">Wait one hour</button></div></form><section id="receipt" class="card" hidden aria-live="polite"></section>
|
||||
<form id="destination-form"><p class="form-heading">Compile a destination without moving there</p><label for="destination-origin">Current location id</label><input id="destination-origin" name="origin_location_id" readonly required><label for="destination-request">Where do you want the world to extend?</label><input id="destination-request" name="destination" maxlength="500" required><button type="submit">Retrieve destination</button></form>
|
||||
<section id="campaign-lab" hidden><h2>Campaign laboratory</h2><div id="campaign-list"></div>
|
||||
<form id="resolution-form"><label for="active-cell-budget">Active Persona-cell budget: <output id="active-cell-budget-value">8</output></label><input id="active-cell-budget" name="active_cell_budget" type="range" min="1" max="32" value="8"><p id="resolution-status"></p><button type="submit">Apply at safe boundary</button></form>
|
||||
<form id="fission-form"><p class="form-heading">Approval-gated population fission</p><label for="fission-parent">Population</label><select name="parent_gestalt_id" id="fission-parent" required></select><label for="fission-axis">Partition axis</label><select id="fission-axis" name="partition_axis"><option value="geography">Geography</option><option value="ideology">Ideology</option><option value="authority">Authority</option><option value="economy_role">Economic role</option><option value="species_body">Species/body</option><option value="information">Information</option></select><label for="fission-values">Named cuts, comma separated</label><input id="fission-values" name="requested_partition_values" required><label for="fission-reason">Why does this population need canonical leaves?</label><input id="fission-reason" name="reason" required><button type="submit">Compile fission preview</button></form>
|
||||
<form id="fission-form"><p class="form-heading">Approval-gated population fission</p><label for="fission-parent">Population</label><select name="parent_gestalt_id" id="fission-parent" required></select><label for="fission-axis">Partition axis</label><select id="fission-axis" name="partition_axis"><option value="geography">Geography</option><option value="ideology">Ideology</option><option value="authority">Authority</option><option value="economy_role">Economic role</option><option value="species_body">Species/body</option><option value="information">Information</option></select><label for="fission-values">Named cuts, comma separated (up to 16)</label><input id="fission-values" name="requested_partition_values" maxlength="2575" required><label for="fission-reason">Why does this population need canonical leaves?</label><input id="fission-reason" name="reason" maxlength="500" required><button type="submit">Compile fission preview</button></form>
|
||||
<form id="fork-form"><label for="fork-name">Fork name</label><input id="fork-name" name="name" maxlength="80" required><button type="submit">Fork selected campaign</button></form><form id="reset-form"><label for="reset-name">Reset branch name</label><input id="reset-name" name="name" maxlength="80" required><button type="submit">Reset from selected branch seed</button></form><a href="/api/campaigns/export" download>Export selected CultCache .cc</a> <a href="/api/campaigns/canon-candidates.md" download>Export canon candidates Markdown</a><details id="operator-lab" hidden><summary>Operator inspector</summary><form id="provider-parallelism-form"><label for="provider-parallelism">Provider request parallelism: <output id="provider-parallelism-value">8</output></label><input id="provider-parallelism" type="range" min="1" max="32" value="8"><button type="submit">Apply operator limit</button></form><button type="button" id="load-operator">Load private state and receipts</button><div id="operator-output"></div></details></section>
|
||||
</main><script type="module" src="/src/main.ts"></script></body></html>
|
||||
|
|
|
|||
|
|
@ -415,10 +415,16 @@ destinationForm.addEventListener("submit", async event => {
|
|||
document.querySelector<HTMLFormElement>("#fission-form")!.addEventListener("submit", async event => {
|
||||
event.preventDefault();
|
||||
const data = new FormData(event.currentTarget as HTMLFormElement);
|
||||
const cuts = String(data.get("requested_partition_values") ?? "").split(",").map(value => value.trim()).filter(Boolean);
|
||||
if (cuts.length > 16 || cuts.some(value => [...value].length > 160)) {
|
||||
status.textContent = "Use at most 16 named cuts, with at most 160 characters in each cut.";
|
||||
document.querySelector<HTMLInputElement>("#fission-values")!.focus();
|
||||
return;
|
||||
}
|
||||
const result = await compilerPost("/api/compiler/gestalt/fission", {
|
||||
parent_gestalt_id: data.get("parent_gestalt_id"),
|
||||
partition_axis: data.get("partition_axis"),
|
||||
requested_partition_values: String(data.get("requested_partition_values") ?? "").split(",").map(value => value.trim()).filter(Boolean),
|
||||
requested_partition_values: cuts,
|
||||
reason: data.get("reason"),
|
||||
});
|
||||
showSummary("Population fission preview", [
|
||||
|
|
|
|||
Loading…
Reference in a new issue