(&self)
| 57 | |
| 58 | impl Command for MemoryNew { |
| 59 | fn run(&self) -> CliResult<()> { |
| 60 | // Reject a bad --kind EARLY (before touching the vault) so `new` never |
| 61 | // writes a non-liftable / non-conforming spine. |
| 62 | if !MEMORY_KIND.contains(&self.kind.as_str()) { |
| 63 | return Err(CliError::InvalidArgument { |
| 64 | message: format!( |
| 65 | "unknown memory kind '{}' (one of {:?})", |
| 66 | self.kind, MEMORY_KIND |
| 67 | ), |
| 68 | }); |
| 69 | } |
| 70 | // status is an equally-closed vocabulary — reject a bad one early too, |
| 71 | // so `new` never persists a non-conforming spine (the gate would only |
| 72 | // catch it later at validate/attest). |
| 73 | if !MEMORY_STATUS.contains(&self.status.as_str()) { |
| 74 | return Err(CliError::InvalidArgument { |
| 75 | message: format!( |
| 76 | "unknown memory status '{}' (one of {:?})", |
| 77 | self.status, MEMORY_STATUS |
| 78 | ), |
| 79 | }); |
| 80 | } |
| 81 | |
| 82 | let root = find_repository_root()?; |
| 83 | let repo = Repository::open(&root).map_err(CliError::Repository)?; |
| 84 | |
| 85 | let id = self.id.clone().unwrap_or_else(generate_ulid_lowercased); |
| 86 | let vault_path = bridge::normalize_memory_path(&id); |
| 87 | |
| 88 | // Build the frontmatter SPINE as FLAT SCALAR/array fields only (so it |
| 89 | // survives yaml_frontmatter_to_json round-trip). Do NOT write |
| 90 | // attributedTo/proof/contentHash — attest fills those. createdAt is |
| 91 | // STABLE (load-bearing: it is signed, so every re-lift must reproduce |
| 92 | // byte-identical signing bytes). |
| 93 | let mut spine = Map::new(); |
| 94 | spine.insert("uid".into(), Value::String(id.clone())); |
| 95 | spine.insert("memoryKind".into(), Value::String(self.kind.clone())); |
| 96 | spine.insert("status".into(), Value::String(self.status.clone())); |
| 97 | spine.insert( |
| 98 | "createdAt".into(), |
| 99 | Value::String(chrono::Utc::now().to_rfc3339()), |
| 100 | ); |
| 101 | if !self.about.is_empty() { |
| 102 | spine.insert( |
| 103 | "about".into(), |
| 104 | Value::Array(self.about.iter().cloned().map(Value::String).collect()), |
| 105 | ); |
| 106 | } |
| 107 | let frontmatter_json = |
| 108 | serde_json::to_string(&spine).expect("frontmatter spine serialization is infallible"); |
| 109 | |
| 110 | // Store the body WITHOUT a frontmatter block; the spine goes through the |
| 111 | // frontmatter_json arg. Enters redb + manifest.memory + merkle exactly |
| 112 | // like the raw `vault memory write` path. |
| 113 | repo.vault_store( |
| 114 | &vault_path, |
| 115 | VaultEntryType::Memory, |
| 116 | MEMORY_SCAFFOLD.as_bytes().to_vec(), |
nothing calls this directly
no test coverage detected