///|
pub fn Mayor::handoff_to_keeper(
self : Mayor,
book : @core.BookRef,
objective : String,
scope_summary : String,
constraints? : Array[String] = [],
) -> @moonclaw.EmbeddedRuntimeHandoff {
ignore(self)
@moonclaw.mayor_to_keeper_handoff(
book.id,
objective,
scope_summary,
constraints~,
)
}
///|
pub async fn Mayor::prepare_keeper_packet(
self : Mayor,
entry : @moonbook.BookCatalogEntry,
task : @moonbook.BookTask,
packet_dir? : String = @moonclaw.default_packet_directory(),
) -> MayorKeeperPacket {
let bundle = @moonbook.hydrate_worker_context(entry, task)
let packet_id = keeper_packet_id(entry.id, task.id)
let profile = @moonclaw.keeper_profile_for_entry_task(entry, task)
let packet = @moonclaw.proposal_packet_from_bundle(
bundle,
task.title,
"Mayor issued a keeper packet for \{entry.name}.",
profile,
packet_id=Some(packet_id),
task=Some(task),
entry=Some(entry),
notes=["produced by mayor runtime", "book task kind=\{task.kind}"],
tags=[entry.id, task.kind, "keeper"],
)
{
handoff: self.handoff_to_keeper(
entry.to_book_ref(),
task.title,
bundle.memory_summary,
constraints=["follow packet-first handoff", "keep book memory isolated"],
),
packet,
packet_path: @moonclaw.packet_file_path(packet_dir, packet.packet_id),
rationale: "Mayor prepared keeper packet \{packet.packet_id} for \{entry.id} / \{task.id}.",
}
}
///|
pub fn keeper_runtime(
book : @core.BookRef,
) -> @moonclaw.EmbeddedMoonclawRuntime {
@moonclaw.keeper_runtime(book)
}
///|
pub fn keeper_packet_id(book_id : String, task_id : String) -> String {
let hash = task_id.hash().to_string().trim(chars="-").to_owned()
let prefix = compact_id_segment(book_id, 24)
let body = compact_id_segment(task_id, 40)
"keeper-\{prefix}-\{body}-\{hash}"
}
///|
fn compact_id_segment(text : String, limit : Int) -> String {
let builder = StringBuilder::new()
let mut emitted = 0
let mut last_dash = false
for char in text.to_lower() {
if (char >= 'a' && char <= 'z') || (char >= '0' && char <= '9') {
if emitted >= limit {
break
}
builder.write_char(char)
emitted += 1
last_dash = false
} else if !last_dash && emitted > 0 {
if emitted >= limit {
break
}
builder.write_char('-')
emitted += 1
last_dash = true
}
}
let slug = builder.to_string().trim(chars="-").to_owned()
if slug.is_blank() {
"task"
} else {
slug
}
}