From 7b811125ca06fe98719577e030c70d2e7bcfc2f6 Mon Sep 17 00:00:00 2001 From: ProofOfConcept Date: Sat, 28 Feb 2026 23:06:27 -0500 Subject: [PATCH] add position field to nodes for stable section ordering MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sections within a file have a natural order that matters — identity.md reads as a narrative, not an alphabetical index. The position field (u32) tracks section index within the file. Set during init and import from parse order. Export and load-context sort by position instead of key, preserving the author's intended structure. --- src/capnp_store.rs | 15 ++++++++++++--- src/main.rs | 19 ++++++++----------- src/migrate.rs | 2 ++ 3 files changed, 22 insertions(+), 14 deletions(-) diff --git a/src/capnp_store.rs b/src/capnp_store.rs index a61361b..4e5b647 100644 --- a/src/capnp_store.rs +++ b/src/capnp_store.rs @@ -98,6 +98,10 @@ pub struct Node { pub last_replayed: f64, pub spaced_repetition_interval: u32, + // Position within file (section index, for export ordering) + #[serde(default)] + pub position: u32, + // Derived fields (not in capnp, computed from graph) #[serde(default)] pub community_id: Option, @@ -488,6 +492,7 @@ impl Store { state_tag: String::new(), last_replayed: 0.0, spaced_repetition_interval: 1, + position: 0, community_id: None, clustering_coefficient: None, schema_fit: None, @@ -566,14 +571,16 @@ impl Store { NodeType::Semantic }; - for unit in &units { + for (pos, unit) in units.iter().enumerate() { seen_keys.insert(unit.key.clone()); if let Some(existing) = self.nodes.get(&unit.key) { - // Update if content changed - if existing.content != unit.content { + // Update if content or position changed + let pos_changed = existing.position != pos as u32; + if existing.content != unit.content || pos_changed { let mut node = existing.clone(); node.content = unit.content.clone(); + node.position = pos as u32; node.version += 1; if let Some(ref state) = unit.state { node.state_tag = state.clone(); @@ -586,6 +593,7 @@ impl Store { } else { let mut node = Store::new_node(&unit.key, &unit.content); node.node_type = node_type; + node.position = pos as u32; if let Some(ref state) = unit.state { node.state_tag = state.clone(); } @@ -1098,6 +1106,7 @@ fn read_content_node(r: memory_capnp::content_node::Reader) -> Result Result<(), String> { .filter(|n| n.key == *key || n.key.starts_with(&prefix)) .collect(); if sections.is_empty() { continue; } - sections.sort_by(|a, b| a.key.cmp(&b.key)); + sections.sort_by(|a, b| a.position.cmp(&b.position)); println!("--- {} ({}) ---", key, label); for node in §ions { @@ -1018,11 +1018,13 @@ fn import_file(store: &mut capnp_store::Store, path: &std::path::Path) -> Result capnp_store::NodeType::Semantic }; - for unit in &units { + for (pos, unit) in units.iter().enumerate() { if let Some(existing) = store.nodes.get(&unit.key) { - if existing.content != unit.content { + let pos_changed = existing.position != pos as u32; + if existing.content != unit.content || pos_changed { let mut node = existing.clone(); node.content = unit.content.clone(); + node.position = pos as u32; node.version += 1; println!(" U {}", unit.key); updated_nodes.push(node); @@ -1030,6 +1032,7 @@ fn import_file(store: &mut capnp_store::Store, path: &std::path::Path) -> Result } else { let mut node = capnp_store::Store::new_node(&unit.key, &unit.content); node.node_type = node_type; + node.position = pos as u32; println!(" + {}", unit.key); new_nodes.push(node); } @@ -1091,15 +1094,9 @@ fn cmd_export(args: &[String]) -> Result<(), String> { continue; } - // Sort: file-level key first (no #), then sections alphabetically + // Sort by position (preserves original file order) sections.sort_by(|a, b| { - let a_is_file = !a.key.contains('#'); - let b_is_file = !b.key.contains('#'); - match (a_is_file, b_is_file) { - (true, false) => std::cmp::Ordering::Less, - (false, true) => std::cmp::Ordering::Greater, - _ => a.key.cmp(&b.key), - } + a.position.cmp(&b.position) }); // Build output: file-level content first, then each section diff --git a/src/migrate.rs b/src/migrate.rs index d73487d..d20c16d 100644 --- a/src/migrate.rs +++ b/src/migrate.rs @@ -227,6 +227,7 @@ pub fn migrate() -> Result<(), String> { state_tag, last_replayed: 0.0, spaced_repetition_interval: 1, + position: 0, community_id: None, clustering_coefficient: None, schema_fit: None, @@ -266,6 +267,7 @@ pub fn migrate() -> Result<(), String> { state_tag: unit.state.clone().unwrap_or_default(), last_replayed: 0.0, spaced_repetition_interval: 1, + position: 0, community_id: None, clustering_coefficient: None, schema_fit: None,