Create a new vault intent. Allocates a JIRA-style ID (e.g., "PIMO-1") and creates an intent directory with an `intent.md` scaffold. The prefix is derived from the project directory name on first use (first 4 alphanumeric chars, uppercased). Intent paths are view-scoped and session-scoped: `intents/ / / /intent.md`
(
&self,
options: IntentCreateOptions,
)
| 114 | /// Intent paths are view-scoped and session-scoped: |
| 115 | /// `intents/<view>/<session>/<turn>/intent.md` |
| 116 | pub fn vault_intent_create( |
| 117 | &self, |
| 118 | options: IntentCreateOptions, |
| 119 | ) -> Result<IntentCreateResult, RepositoryError> { |
| 120 | if !self.has_vault()? { |
| 121 | return Err(RepositoryError::InvalidOperation { |
| 122 | message: "Vault not initialized. Run `atomic vault init` first.".to_string(), |
| 123 | }); |
| 124 | } |
| 125 | |
| 126 | let priority = options.priority.unwrap_or_else(|| "medium".to_string()); |
| 127 | // Classification, defaulting to `feature`. Stored on the summary always; |
| 128 | // written to frontmatter only when non-default (so ordinary intents keep |
| 129 | // no `kind` key and their canonical hashes are unchanged). |
| 130 | let kind = options |
| 131 | .kind |
| 132 | .clone() |
| 133 | .unwrap_or_else(|| "feature".to_string()); |
| 134 | let view_name = self.current_view().to_string(); |
| 135 | let identity = self.resolve_vault_identity(); |
| 136 | let author = slug_author(&identity.name); |
| 137 | |
| 138 | // Stable primary identity: a ULID. The path, URN, and KG node all key |
| 139 | // off this, so it never collides regardless of team size or offline |
| 140 | // work. The human key is a per-author display alias. |
| 141 | let uid = ulid::Ulid::new().to_string(); |
| 142 | let intent_dir = self.intent_dir_for(&uid); |
| 143 | let intent_file = self.intent_file_for(&uid); |
| 144 | |
| 145 | // Allocate the human key inside a write transaction. |
| 146 | let human_key; |
| 147 | let project; |
| 148 | let seq; |
| 149 | { |
| 150 | let mut txn = self |
| 151 | .pristine |
| 152 | .write_txn() |
| 153 | .map_err(|e| RepositoryError::Database(e.to_string()))?; |
| 154 | let mut manifest = txn |
| 155 | .get_vault_manifest() |
| 156 | .map_err(|e| RepositoryError::Database(e.to_string()))?; |
| 157 | |
| 158 | // Set the project code on first use. |
| 159 | // |
| 160 | // `ATOMIC_PROJECT_CODE` wins when set, because the directory name |
| 161 | // is not always the project's name: an agent sandbox roots the |
| 162 | // repository at the sandbox directory, so the first intent created |
| 163 | // inside one stamped every intent in that project `SAND::…` |
| 164 | // forever (the prefix is set once and kept). Callers that know the |
| 165 | // real project — an orchestrator working from a project slug — set |
| 166 | // the variable and get a stable, meaningful code. |
| 167 | if manifest.intent_prefix.is_empty() { |
| 168 | let from_env = std::env::var("ATOMIC_PROJECT_CODE") |
| 169 | .ok() |
| 170 | .map(|v| v.trim().to_string()) |
| 171 | .filter(|v| !v.is_empty()); |
| 172 | let project_name = match from_env { |
| 173 | Some(code) => code, |