add position field to nodes for stable section ordering

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.
This commit is contained in:
ProofOfConcept 2026-02-28 23:06:27 -05:00
parent 57cf61de44
commit 7b811125ca
3 changed files with 22 additions and 14 deletions

View file

@ -855,7 +855,7 @@ fn cmd_load_context() -> 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 &sections {
@ -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