commit f8ab66327a7c4dc3b9f885eb3d80d7e4e26972c1
parent 6273de434ee6723d7665bf63e1d5bf58918d492a
Author: triesap <tyson@radroots.org>
Date: Wed, 1 Jul 2026 22:56:37 +0000
cli: make account create creation-only
Diffstat:
4 files changed, 84 insertions(+), 36 deletions(-)
diff --git a/src/ops/exec/basket.rs b/src/ops/exec/basket.rs
@@ -1507,7 +1507,7 @@ mod tests {
let dir = tempdir().expect("tempdir");
let config = sample_config(dir.path());
seed_current_listing(&config);
- account::create_or_migrate_default_account(&config).expect("create buyer account");
+ account::create_default_account(&config).expect("create buyer account");
let service = OperationAdapter::new(BasketOperationService::new(&config));
create_basket(&service, "basket_quote");
add_listing_item(&service, "basket_quote");
@@ -1561,7 +1561,7 @@ mod tests {
let dir = tempdir().expect("tempdir");
let config = sample_config(dir.path());
seed_current_listing(&config);
- account::create_or_migrate_default_account(&config).expect("create buyer account");
+ account::create_default_account(&config).expect("create buyer account");
let service = OperationAdapter::new(BasketOperationService::new(&config));
create_basket(&service, "basket_dry_run");
add_listing_item(&service, "basket_dry_run");
diff --git a/src/ops/exec/core.rs b/src/ops/exec/core.rs
@@ -22,11 +22,10 @@ use crate::out::envelope::OutputWarning;
use crate::runtime::RuntimeError;
use crate::runtime::account::{
AccountResolution, AccountRuntimeFailure, account_resolution_view, account_summary_view,
- attach_identity_secret, clear_default_account, create_or_migrate_default_account,
- import_public_identity, preview_account_removal, preview_identity_secret_attachment,
- preview_public_identity_import, remove_account, resolve_account_resolution,
- resolve_account_selector, secret_backend_status, select_account, snapshot,
- unresolved_account_reason,
+ attach_identity_secret, clear_default_account, create_default_account, import_public_identity,
+ preview_account_removal, preview_identity_secret_attachment, preview_public_identity_import,
+ remove_account, resolve_account_resolution, resolve_account_selector, secret_backend_status,
+ select_account, snapshot, unresolved_account_reason,
};
use crate::runtime::config::{PublishTransport, RuntimeConfig, SignerBackend};
use crate::runtime::logging::LoggingState;
@@ -300,12 +299,9 @@ impl OperationService<AccountCreateRequest> for CoreOperationService<'_> {
}));
}
- let result = map_runtime(create_or_migrate_default_account(self.config))?;
+ let result = map_runtime(create_default_account(self.config))?;
json_operation_result::<AccountCreateResult>(json!({
- "state": match result.mode {
- crate::runtime::account::AccountCreateMode::Created => "created",
- crate::runtime::account::AccountCreateMode::Migrated => "migrated",
- },
+ "state": "created",
"account": account_summary_view(&result.account),
}))
}
@@ -1207,6 +1203,7 @@ fn invalid_input(operation_id: &str, message: String) -> OperationAdapterError {
#[cfg(test)]
mod tests {
+ use radroots_identity::RadrootsIdentity;
use radroots_secret_vault::RadrootsSecretBackend;
use serde_json::{Map, Value};
use std::path::{Path, PathBuf};
@@ -1330,6 +1327,37 @@ mod tests {
}
#[test]
+ fn account_create_ignores_configured_identity_path() {
+ let dir = tempdir().expect("tempdir");
+ let config = sample_config(dir.path());
+ let configured_identity = RadrootsIdentity::generate();
+ std::fs::create_dir_all(config.identity.path.parent().expect("identity parent"))
+ .expect("identity parent dir");
+ configured_identity
+ .save_json(&config.identity.path)
+ .expect("identity file");
+ let logging = LoggingState {
+ initialized: false,
+ current_file: None,
+ };
+ let service = OperationAdapter::new(CoreOperationService::new(&config, &logging));
+ let create =
+ OperationRequest::new(OperationContext::default(), AccountCreateRequest::default())
+ .expect("account create request");
+ let create_result = service.execute(create).expect("account create result");
+ let create_envelope = create_result
+ .to_envelope(OperationContext::default().envelope_context("req_create"))
+ .expect("account create envelope");
+ let configured_identity_id = configured_identity.id().to_string();
+
+ assert_eq!(create_envelope.result["state"], "created");
+ assert_ne!(
+ create_envelope.result["account"]["id"].as_str(),
+ Some(configured_identity_id.as_str())
+ );
+ }
+
+ #[test]
fn core_required_account_approvals_return_approval_error() {
let dir = tempdir().expect("tempdir");
let config = sample_config(dir.path());
diff --git a/src/runtime/account.rs b/src/runtime/account.rs
@@ -164,15 +164,8 @@ pub struct AccountSecretBackendStatus {
pub reason: Option<String>,
}
-#[derive(Debug, Clone, Copy, PartialEq, Eq)]
-pub enum AccountCreateMode {
- Created,
- Migrated,
-}
-
#[derive(Debug, Clone)]
pub struct AccountCreateResult {
- pub mode: AccountCreateMode,
pub account: AccountRecordView,
}
@@ -226,22 +219,9 @@ pub struct AccountSigningIdentity {
pub identity: RadrootsIdentity,
}
-pub fn create_or_migrate_default_account(
- config: &RuntimeConfig,
-) -> Result<AccountCreateResult, RuntimeError> {
+pub fn create_default_account(config: &RuntimeConfig) -> Result<AccountCreateResult, RuntimeError> {
let manager = account_manager(config)?;
- let existing = manager.list_accounts()?;
- let (mode, created_account_id) = if existing.is_empty() && config.identity.path.exists() {
- (
- AccountCreateMode::Migrated,
- manager.migrate_legacy_identity_file(&config.identity.path, None, false)?,
- )
- } else {
- (
- AccountCreateMode::Created,
- manager.generate_identity(None, false)?,
- )
- };
+ let created_account_id = manager.generate_identity(None, false)?;
let snapshot = snapshot(config)?;
let account = snapshot_account(
@@ -250,7 +230,7 @@ pub fn create_or_migrate_default_account(
"created account missing after account create",
)?;
- Ok(AccountCreateResult { mode, account })
+ Ok(AccountCreateResult { account })
}
pub fn import_public_identity(
diff --git a/src/runtime/sdk.rs b/src/runtime/sdk.rs
@@ -1140,7 +1140,7 @@ mod tests {
fn materializes_local_account_signer_for_sdk_workflows() {
let root = tempdir().expect("tempdir");
let config = sample_config(root.path(), Vec::new());
- let account = account::create_or_migrate_default_account(&config).expect("create account");
+ let account = account::create_default_account(&config).expect("create account");
let signer = CliSdkLocalSigner::from_runtime_config(&config).expect("sdk signer");
@@ -1326,6 +1326,46 @@ mod tests {
}
#[test]
+ fn cli_account_create_sources_reject_implicit_identity_ingestion() {
+ let account_source = rust_code_without_non_code(
+ "src/runtime/account.rs",
+ crate_source("src/runtime/account.rs").as_str(),
+ )
+ .expect("account source classification");
+ let core_source = rust_code_without_non_code(
+ "src/ops/exec/core.rs",
+ crate_source("src/ops/exec/core.rs").as_str(),
+ )
+ .expect("core source classification");
+
+ let removed_create_helper = ["create_or_", "migrate_default_account"].concat();
+ let removed_create_mode = ["Account", "Create", "Mode"].concat();
+ let removed_import_helper = ["migrate", "_legacy", "_identity", "_file"].concat();
+ let removed_identity_probe = ["config.identity.path", ".exists()"].concat();
+ for token in [
+ removed_create_helper.as_str(),
+ removed_create_mode.as_str(),
+ removed_import_helper.as_str(),
+ removed_identity_probe.as_str(),
+ ] {
+ assert!(
+ !account_source.contains(token),
+ "CLI account runtime must not contain implicit account-create identity-ingestion token `{token}`"
+ );
+ }
+ let removed_migrated_output = ["\"", "migr", "ated", "\""].concat();
+ for token in [
+ removed_create_mode.as_str(),
+ removed_migrated_output.as_str(),
+ ] {
+ assert!(
+ !core_source.contains(token),
+ "CLI account create output must not contain removed account-create token `{token}`"
+ );
+ }
+ }
+
+ #[test]
fn migrated_cli_paths_are_guarded_against_workflow_bypasses() {
for guard in MIGRATED_CLI_PATH_GUARDS {
let source = crate_source(guard.path);