///|
pub(all) enum LegacySourceKind {
LegacyAnalysisRecord
LegacyPendingRecord
LegacyExperienceEntry
LegacyTradeLog
} derive(Debug, Eq, ToJson, FromJson)
///|
pub(all) enum ImportDisposition {
ImportReady
NeedsReview
SkippedUnsupported
} derive(Debug, Eq, ToJson, FromJson)
///|
pub(all) struct LegacySourceRef {
source_path : String
kind : LegacySourceKind
schema_version : String
target_run_id : @domain.RunId?
trace_id : String
} derive(Debug, Eq, ToJson, FromJson)
///|
pub(all) struct LegacyImportBatchInput {
batch_id : String
source_root : String
sources : Array[LegacySourceRef]
} derive(Debug, Eq, ToJson, FromJson)
///|
pub(all) struct LegacyImportPlanItem {
source : LegacySourceRef
target_path : String
target_schema_id : String
artifact_kind : @book.BookArtifactKind
disposition : ImportDisposition
review_message : String?
} derive(Debug, Eq, ToJson, FromJson)
///|
pub(all) struct LegacyImportPlan {
batch_id : String
source_root : String
ready_count : Int
review_count : Int
skipped_count : Int
items : Array[LegacyImportPlanItem]
write_plan : @book.BookWritePlan
} derive(Debug, Eq, ToJson, FromJson)
///|
pub(all) enum ImportedRecordKind {
ImportedAnalysis
ImportedPendingAnalysis
ImportedExperience
ImportedTrade
} derive(Debug, Eq, ToJson, FromJson)
///|
pub(all) struct ImportedMoonBookRecord {
trace_id : String
source_path : String
target_path : String
schema_version : String
schema_id : String
kind : ImportedRecordKind
title : String
inspectable_summary : String
replay_anchor_path : String
} derive(Debug, Eq, ToJson, FromJson)
///|
pub(all) struct LegacyImportProjection {
batch_id : String
record_count : Int
review_count : Int
records : Array[ImportedMoonBookRecord]
review_paths : Array[String]
write_plan : @book.BookWritePlan
summary : String
} derive(Debug, Eq, ToJson, FromJson)
///|
pub fn legacy_source_ref(
source_path : String,
kind : LegacySourceKind,
trace_id : String,
schema_version? : String = "pa-agent-v1",
target_run_id? : @domain.RunId,
) -> LegacySourceRef {
{ source_path, kind, schema_version, target_run_id, trace_id }
}
///|
pub fn legacy_import_batch_input(
batch_id : String,
source_root : String,
sources : Array[LegacySourceRef],
) -> LegacyImportBatchInput {
{ batch_id, source_root, sources }
}
///|
fn source_key(source : LegacySourceRef) -> String {
match source.target_run_id {
Some(run_id) => run_id
None => source.trace_id
}
}
///|
fn target_schema_id(kind : LegacySourceKind) -> String {
match kind {
LegacyAnalysisRecord | LegacyPendingRecord => "analysis-record"
LegacyExperienceEntry => "legacy-experience-entry"
LegacyTradeLog => "legacy-trade-log"
}
}
///|
fn artifact_kind(kind : LegacySourceKind) -> @book.BookArtifactKind {
match kind {
LegacyAnalysisRecord | LegacyPendingRecord => ImportedLegacyRecord
LegacyExperienceEntry => ImportedExperienceMemory
LegacyTradeLog => ImportedTradeLog
}
}
///|
fn target_path(source : LegacySourceRef) -> String {
let key = source_key(source)
match source.kind {
LegacyAnalysisRecord => "records/imported/analyses/\{key}.json"
LegacyPendingRecord => "records/imported/pending/\{key}.json"
LegacyExperienceEntry => "wiki/imported-experience/\{key}.json"
LegacyTradeLog => "records/imported/trade-logs/\{key}.json"
}
}
///|
fn disposition(source : LegacySourceRef) -> (ImportDisposition, String?) {
if source.source_path == "" {
(SkippedUnsupported, Some("source path is required"))
} else if source.trace_id == "" {
(SkippedUnsupported, Some("trace id is required for idempotent import"))
} else if source.schema_version == "" {
(NeedsReview, Some("legacy schema version is unknown"))
} else {
match source.kind {
LegacyAnalysisRecord | LegacyPendingRecord =>
match source.target_run_id {
Some(_) => (ImportReady, None)
None =>
(
NeedsReview,
Some("analysis and pending records need a target run id"),
)
}
LegacyExperienceEntry | LegacyTradeLog => (ImportReady, None)
}
}
}
///|
fn plan_item(source : LegacySourceRef) -> LegacyImportPlanItem {
let (item_disposition, review_message) = disposition(source)
{
source,
target_path: target_path(source),
target_schema_id: target_schema_id(source.kind),
artifact_kind: artifact_kind(source.kind),
disposition: item_disposition,
review_message,
}
}
///|
fn count_disposition(
items : Array[LegacyImportPlanItem],
value : ImportDisposition,
) -> Int {
items.fold(init=0, fn(count, item) {
if item.disposition == value {
count + 1
} else {
count
}
})
}
///|
fn import_manifest_ref(batch_id : String) -> @book.BookArtifactRef {
@book.book_artifact_ref(
batch_id,
"records/imports/\{batch_id}.json",
"legacy-import-plan",
GeneratedImportManifest,
"idempotent MoonBook import manifest for legacy PA Agent sources",
)
}
///|
fn item_artifact_ref(
batch_id : String,
item : LegacyImportPlanItem,
) -> @book.BookArtifactRef {
@book.book_artifact_ref(
batch_id,
item.target_path,
item.target_schema_id,
item.artifact_kind,
"imported from \{item.source.source_path}",
)
}
///|
fn source_kind_label(kind : LegacySourceKind) -> String {
match kind {
LegacyAnalysisRecord => "analysis"
LegacyPendingRecord => "pending analysis"
LegacyExperienceEntry => "experience memory"
LegacyTradeLog => "trade log"
}
}
///|
fn imported_kind(kind : LegacySourceKind) -> ImportedRecordKind {
match kind {
LegacyAnalysisRecord => ImportedAnalysis
LegacyPendingRecord => ImportedPendingAnalysis
LegacyExperienceEntry => ImportedExperience
LegacyTradeLog => ImportedTrade
}
}
///|
fn imported_record(
source_root : String,
item : LegacyImportPlanItem,
) -> ImportedMoonBookRecord {
let source_path = "\{source_root}/\{item.source.source_path}"
{
trace_id: item.source.trace_id,
source_path,
target_path: item.target_path,
schema_version: item.source.schema_version,
schema_id: item.target_schema_id,
kind: imported_kind(item.source.kind),
title: "Imported PA Agent \{source_kind_label(item.source.kind)}",
inspectable_summary: "Imported \{source_kind_label(item.source.kind)} from \{source_path} into \{item.target_path}",
replay_anchor_path: "records/replays/imported/\{item.source.trace_id}.json",
}
}
///|
fn review_path(item : LegacyImportPlanItem) -> String {
"records/reviews/imports/\{item.source.trace_id}.json"
}
///|
fn imported_records(plan : LegacyImportPlan) -> Array[ImportedMoonBookRecord] {
let records : Array[ImportedMoonBookRecord] = []
for item in plan.items {
if item.disposition is ImportReady {
records.push(imported_record(plan.source_root, item))
}
}
records
}
///|
fn import_review_paths(plan : LegacyImportPlan) -> Array[String] {
let paths : Array[String] = []
for item in plan.items {
if item.disposition is NeedsReview || item.disposition is SkippedUnsupported {
paths.push(review_path(item))
}
}
paths
}
///|
fn write_plan(
batch_id : String,
items : Array[LegacyImportPlanItem],
) -> @book.BookWritePlan {
let artifacts = [import_manifest_ref(batch_id)]
for item in items {
if item.disposition is ImportReady {
artifacts.push(item_artifact_ref(batch_id, item))
}
}
{ run_id: batch_id, artifacts }
}
///|
pub fn prepare_legacy_import_plan(
input : LegacyImportBatchInput,
) -> LegacyImportPlan {
let items = input.sources.map(fn(source) { plan_item(source) })
{
batch_id: input.batch_id,
source_root: input.source_root,
ready_count: count_disposition(items, ImportReady),
review_count: count_disposition(items, NeedsReview),
skipped_count: count_disposition(items, SkippedUnsupported),
items,
write_plan: write_plan(input.batch_id, items),
}
}
///|
pub fn materialize_legacy_import_projection(
plan : LegacyImportPlan,
) -> LegacyImportProjection {
let records = imported_records(plan)
let review_paths = import_review_paths(plan)
{
batch_id: plan.batch_id,
record_count: records.length(),
review_count: review_paths.length(),
records,
review_paths,
write_plan: plan.write_plan,
summary: if review_paths.is_empty() {
"legacy import projection materialized \{records.length()} inspectable record(s)"
} else {
"legacy import projection has \{review_paths.length()} review path(s)"
},
}
}