journal: remove all stringly-typed key patterns, use NodeType

- journal_new: key is slugified title (agent names things properly)
- journal_tail: sort by created_at (immutable), not timestamp (mutable)
- journal_update: find latest by created_at
- {{latest_journal}}: query by NodeType::EpisodicSession, not "journal" key
- poc-memory journal write: requires a name argument
- Removed all journal#j-{timestamp}-{slug} patterns from:
  - prompts.rs (rename candidates)
  - graph.rs (date extraction, organize skip list)
  - cursor.rs (date extraction)
  - store/mod.rs (doc comment)
- graph.rs organize: filter by NodeType::Semantic instead of key prefix
- cursor.rs: use created_at for date extraction instead of key parsing

Co-Authored-By: Kent Overstreet <kent.overstreet@linux.dev>
This commit is contained in:
ProofOfConcept 2026-03-26 19:11:17 -04:00
parent 85fa54cba9
commit eac59b423e
9 changed files with 63 additions and 67 deletions

View file

@ -1,7 +1,7 @@
// cli/journal.rs — journal subcommand handlers
pub fn cmd_tail(n: usize, full: bool, provenance: Option<&str>) -> Result<(), String> {
pub fn cmd_tail(n: usize, full: bool, provenance: Option<&str>, dedup: bool) -> Result<(), String> {
let path = crate::store::nodes_path();
if !path.exists() {
return Err("No node log found".into());
@ -24,11 +24,21 @@ pub fn cmd_tail(n: usize, full: bool, provenance: Option<&str>) -> Result<(), St
}
}
// Filter by provenance if specified (prefix match)
// Filter by provenance if specified (substring match)
if let Some(prov) = provenance {
entries.retain(|n| n.provenance.contains(prov));
}
// Dedup: keep only the latest version of each key
if dedup {
let mut seen = std::collections::HashSet::new();
// Walk backwards so we keep the latest
entries = entries.into_iter().rev()
.filter(|n| seen.insert(n.key.clone()))
.collect();
entries.reverse();
}
let start = entries.len().saturating_sub(n);
for node in &entries[start..] {
let ts = if node.timestamp > 0 && node.timestamp < 4_000_000_000 {
@ -172,27 +182,23 @@ pub fn cmd_journal_tail(n: usize, full: bool, level: u8) -> Result<(), String> {
}
}
pub fn cmd_journal_write(text: &[String]) -> Result<(), String> {
pub fn cmd_journal_write(name: &str, text: &[String]) -> Result<(), String> {
if text.is_empty() {
return Err("journal-write requires text".into());
return Err("journal write requires text".into());
}
super::check_dry_run();
let text = text.join(" ");
let timestamp = crate::store::format_datetime(crate::store::now_epoch());
let content = format!("## {}{}\n\n{}", timestamp, name, text);
let slug: String = text.split_whitespace()
.take(6)
let key: String = name.split_whitespace()
.map(|w| w.to_lowercase()
.chars().filter(|c| c.is_alphanumeric() || *c == '-')
.collect::<String>())
.filter(|s| !s.is_empty())
.collect::<Vec<_>>()
.join("-");
let slug = if slug.len() > 50 { &slug[..50] } else { &slug };
let key = format!("journal#j-{}-{}", timestamp.to_lowercase().replace(':', "-"), slug);
let content = format!("## {}\n\n{}", timestamp, text);
let source_ref = find_current_transcript();