///|
pub struct MoonCodeBookResultImport {
key : String
session_id : String
command_id : String
result_id : String
summary : String
artifacts : Array[String]
review_queued : Bool
notify_town : Bool
evidence_id : String
updated_pages : Array[String]
executable_event_path : String
latest_executable_event_path : String
executable_event : @moonbook_contracts.ExecutableBookEvent
} derive(Debug, Eq, ToJson, FromJson)
///|
fn mooncode_book_results_sessions_root(book_root : String) -> String {
@moonsuite.product_artifact_for_book_root(book_root, "mooncode", "sessions")
}
///|
fn mooncode_book_results_processed_ledger(entry : BookCatalogEntry) -> String {
@storage.moontown_product_artifact(
"mooncode-book-results/\{safe_mooncode_book_result_id(entry.id)}/processed.jsonl",
)
}
///|
fn safe_mooncode_book_result_id(book_id : String) -> String {
let builder = StringBuilder::new()
for ch in book_id {
if (ch >= 'a' && ch <= 'z') ||
(ch >= 'A' && ch <= 'Z') ||
(ch >= '0' && ch <= '9') ||
ch == '-' ||
ch == '_' {
builder.write_char(ch)
} else {
builder.write_char('-')
}
}
let safe = builder.to_string().trim(chars="-").to_owned()
if safe.is_empty() {
"book"
} else {
safe
}
}
///|
pub async fn reconcile_mooncode_book_result_sidecars(
entry : BookCatalogEntry,
moonbook_root? : String = default_moonbook_root(),
) -> Array[MoonCodeBookResultImport] {
let root = resolved_workspace_root(entry.workspace_root)
let sessions_root = mooncode_book_results_sessions_root(root)
if !@fs.exists(sessions_root) {
return []
}
let ledger_path = mooncode_book_results_processed_ledger(entry)
let processed = read_mooncode_book_result_processed_keys(ledger_path)
let imports : Array[MoonCodeBookResultImport] = []
for session_dir in @support.list_directory_paths(sessions_root) {
let session_id = @support.path_basename(session_dir)
let log_path = "\{session_dir}/book-results.jsonl"
let log_text = @support.read_optional_text(log_path).unwrap_or("")
if log_text.trim().is_empty() {
continue
}
for raw in log_text.split("\n") {
let line = raw.trim()
if line.is_empty() {
continue
}
let payload = @json.parse(line) catch { _ => continue }
let result = match decode_book_result_payload(payload) {
Some(result) => result
None => continue
}
let command_id = mooncode_sidecar_string(payload, "command_id").unwrap_or(
result.result_id,
)
let key = mooncode_book_result_processed_key(
payload,
session_id,
command_id,
result.result_id,
)
if processed.contains(key) {
continue
}
let outcome = persist_result_payload(entry, payload, moonbook_root~)
processed.push(key)
write_mooncode_book_result_processed_keys(ledger_path, processed)
imports.push({
key,
session_id,
command_id,
result_id: result.result_id,
summary: result.summary,
artifacts: result.artifacts,
review_queued: outcome.review_queued,
notify_town: outcome.notify_town,
evidence_id: outcome.evidence_id,
updated_pages: outcome.updated_pages,
executable_event_path: outcome.executable_event_path,
latest_executable_event_path: outcome.latest_executable_event_path,
executable_event: outcome.executable_event,
})
}
}
imports
}
///|
async fn read_mooncode_book_result_processed_keys(
path : String,
) -> Array[String] {
let keys : Array[String] = []
let content = @support.read_text_or_empty(path)
for raw in content.split("\n") {
let key = raw.trim().to_owned()
if !key.is_empty() && !keys.contains(key) {
keys.push(key)
}
}
keys
}
///|
async fn write_mooncode_book_result_processed_keys(
path : String,
keys : Array[String],
) -> Unit {
@support.write_text_file(path, keys.join("\n") + "\n")
}
///|
fn mooncode_book_result_processed_key(
payload : Json,
session_id : String,
command_id : String,
task_id : String,
) -> String {
mooncode_sidecar_string(payload, "result_path").unwrap_or(
"\{session_id}:\{command_id}:\{task_id}",
)
}
///|
fn mooncode_sidecar_string(payload : Json, field : String) -> String? {
match payload {
Object(fields) =>
match fields.get(field) {
Some(String(value)) => Some(value)
_ => None
}
_ => None
}
}