Desk door: DeskForkTooLarge becomes the not-stored refusal fork_too_large (list only), text names fork id and order; schemas regenerated

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
Eureka 2026-11-11 00:56:36 +00:00
commit c2aef3bc58
5 changed files with 18 additions and 91 deletions

View file

@ -152,13 +152,14 @@ pub struct ForkDeskAnswer {
pub request_id: String,
}
/// The three requests Huginn refuses without storing, by code.
/// The refusals Huginn sends without storing, by code. `ForkTooLarge` is the list's alone.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum ForkRefusalCode {
ForkUnknown,
BodyInvalid,
RequestIdConflict,
ForkTooLarge,
}
/// `gamecult.fork.refusal.v0`. `error` is a fixed sentence, never an echo of what was sent;
@ -382,6 +383,12 @@ fn fork_unknown() -> ForkRefusal {
fork_refusal(ForkRefusalCode::ForkUnknown, "fork_unknown: no such fork".into())
}
/// A list page whose first entry alone is past the page budget: names that fork and its `order`; a
/// client continues with the cursor `c<order>` and has reached the end at order 0.
fn fork_too_large(fork: &str, order: u64) -> ForkRefusal {
fork_refusal(ForkRefusalCode::ForkTooLarge, format!("fork_too_large: fork {fork} at order {order} is past the page budget by itself; continue with cursor c{order}"))
}
fn body_invalid(field: &str) -> ForkRefusal {
fork_refusal(ForkRefusalCode::BodyInvalid, format!("body_invalid: the body does not satisfy gamecult.fork.answer_request.v0 ({field})"))
}

View file

@ -326,7 +326,7 @@ impl<S: MindStore> Mind<S> {
/// A page holds at most `limit` entries (never more than 50) and stops short only when the next
/// entry would push the encoded response past `DESK_PAGE_BUDGET` (the one `DESK_MAX_RESPONSE_BYTES`
/// less the envelope margin); counts and stamp are unaffected. A page whose FIRST entry alone is past
/// the budget is `MindRefusal::DeskForkTooLarge`, naming that fork and its order: the caller goes on
/// the budget is the `fork_too_large` refusal, naming that fork and its order: the caller goes on
/// with the cursor `c<order>`, which returns the forks below it, so no older fork is unreachable.
/// The fork itself is still its whole entry on `desk_fork`. Nothing is dropped silently.
///
@ -384,7 +384,7 @@ impl<S: MindStore> Mind<S> {
if bytes > DESK_PAGE_BUDGET {
let list = held(&mut response);
if list.forks.len() == 1 {
return Ok(DeskResponse::Refused(MindRefusal::DeskForkTooLarge { fork: key.to_string(), order: *order, bytes: bytes as u64, limit: DESK_PAGE_BUDGET as u64 }));
return Ok(DeskResponse::NotStored(fork_too_large(key, *order)));
}
list.forks.pop();
list.next_cursor = before;
@ -1061,9 +1061,11 @@ mod tests {
assert_eq!(ids(&first), [fork(3)]);
assert_eq!(first.next_cursor.map(ForkCursor::order), Some(2));
let response = mind.desk_forks(&list(&LIST), None, first.next_cursor, limit(50));
let DeskResponse::Refused(MindRefusal::DeskForkTooLarge { fork: named, order, bytes, limit: bound }) = response else { panic!("{response:?}") };
assert_eq!((named, order, bound), (fork(2), 1, DESK_PAGE_BUDGET as u64));
assert!(bytes > bound);
let DeskResponse::NotStored(refusal) = response else { panic!("{response:?}") };
assert_eq!(refusal.code, ForkRefusalCode::ForkTooLarge);
let order = 1;
assert_eq!(refusal.error, fork_too_large(&fork(2), order).error);
assert!(refusal.error.contains(&fork(2)) && refusal.error.contains("order 1"), "{refusal:?}");
// The order the refusal names is the order of the fork it names; its cursor goes on past it.
assert_eq!(entry(mind.desk_fork(&list(&LIST), &fork(2))).0.card.order, order);
let (rest, _) = page(mind.desk_forks(&list(&LIST), None, ForkCursor::new(order), limit(50)));

View file

@ -109,11 +109,6 @@ pub enum MindRefusal {
/// truncated or paginated on its behalf. The daemon owns it and its
/// deferral bound decides it; no rule of the mind is involved.
ResponseTooLarge { bytes: u64, limit: u64 },
/// A desk page whose first entry alone is past the page budget: `fork` is that fork, `order` its
/// rank (a caller goes on past it with the cursor `c<order>`, which returns the forks below it),
/// `bytes` what the page holding it would encode to (without the envelope) and `limit` the page
/// budget it exceeds. Raised by the mind's page fill, which is why it is not `ResponseTooLarge`.
DeskForkTooLarge { fork: String, order: u64, bytes: u64, limit: u64 },
/// A selection the substrate or the organ's value door refused: `field`
/// is the selection field the client sent, `value` the offending part.
SelectionInvalid { field: String, value: Option<String>, message: String },

View file

@ -471,12 +471,13 @@
]
},
"ForkRefusalCode": {
"description": "The three requests Huginn refuses without storing, by code.",
"description": "The refusals Huginn sends without storing, by code. `ForkTooLarge` is the list's alone.",
"type": "string",
"enum": [
"fork_unknown",
"body_invalid",
"request_id_conflict"
"request_id_conflict",
"fork_too_large"
]
},
"ListSchema": {
@ -1562,45 +1563,6 @@
"ResponseTooLarge"
]
},
{
"description": "A desk page whose first entry alone is past the page budget: `fork` is that fork, `order` its\nrank (a caller goes on past it with the cursor `c<order>`, which returns the forks below it),\n`bytes` what the page holding it would encode to (without the envelope) and `limit` the page\nbudget it exceeds. Raised by the mind's page fill, which is why it is not `ResponseTooLarge`.",
"type": "object",
"properties": {
"DeskForkTooLarge": {
"type": "object",
"properties": {
"bytes": {
"type": "integer",
"format": "uint64",
"minimum": 0
},
"fork": {
"type": "string"
},
"limit": {
"type": "integer",
"format": "uint64",
"minimum": 0
},
"order": {
"type": "integer",
"format": "uint64",
"minimum": 0
}
},
"required": [
"fork",
"order",
"bytes",
"limit"
]
}
},
"additionalProperties": false,
"required": [
"DeskForkTooLarge"
]
},
{
"description": "A selection the substrate or the organ's value door refused: `field`\nis the selection field the client sent, `value` the offending part.",
"type": "object",

View file

@ -2059,45 +2059,6 @@
"ResponseTooLarge"
]
},
{
"description": "A desk page whose first entry alone is past the page budget: `fork` is that fork, `order` its\nrank (a caller goes on past it with the cursor `c<order>`, which returns the forks below it),\n`bytes` what the page holding it would encode to (without the envelope) and `limit` the page\nbudget it exceeds. Raised by the mind's page fill, which is why it is not `ResponseTooLarge`.",
"type": "object",
"properties": {
"DeskForkTooLarge": {
"type": "object",
"properties": {
"bytes": {
"type": "integer",
"format": "uint64",
"minimum": 0
},
"fork": {
"type": "string"
},
"limit": {
"type": "integer",
"format": "uint64",
"minimum": 0
},
"order": {
"type": "integer",
"format": "uint64",
"minimum": 0
}
},
"required": [
"fork",
"order",
"bytes",
"limit"
]
}
},
"additionalProperties": false,
"required": [
"DeskForkTooLarge"
]
},
{
"description": "A selection the substrate or the organ's value door refused: `field`\nis the selection field the client sent, `value` the offending part.",
"type": "object",