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

@ -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<u32>,
@ -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<Node, Stri
state_tag: read_text(r.get_state_tag()),
last_replayed: r.get_last_replayed(),
spaced_repetition_interval: r.get_spaced_repetition_interval(),
position: 0,
community_id: None,
clustering_coefficient: None,
schema_fit: None,