Sync public tree for logout alias

This commit is contained in:
Michel Paulissen 2026-07-03 23:59:58 +02:00
parent 22fa4fc675
commit 1c3fdfc38a
4 changed files with 55 additions and 3 deletions

View file

@ -1,7 +1,7 @@
{ {
"kind": "disasmer-filtered-public-tree", "kind": "disasmer-filtered-public-tree",
"source_commit": "0dc80fb7a350886260d9504cd7daf6d122d3a6a5", "source_commit": "d0ea56e226f6a5fa409ab5284d5c4e550afe2f35",
"release_name": "dryrun-0dc80fb7a350", "release_name": "dryrun-d0ea56e226f6",
"filtered_out": [ "filtered_out": [
"private/**", "private/**",
"experiments/**", "experiments/**",

View file

@ -36,6 +36,7 @@ struct Cli {
enum Commands { enum Commands {
Doctor(DoctorArgs), Doctor(DoctorArgs),
Login(LoginArgs), Login(LoginArgs),
Logout(AuthLogoutArgs),
Auth { Auth {
#[command(subcommand)] #[command(subcommand)]
command: AuthCommands, command: AuthCommands,
@ -819,6 +820,10 @@ fn auth_status_report(args: AuthStatusArgs, cwd: PathBuf) -> Result<Value> {
} }
fn auth_logout_report(args: AuthLogoutArgs, cwd: PathBuf) -> Result<Value> { fn auth_logout_report(args: AuthLogoutArgs, cwd: PathBuf) -> Result<Value> {
logout_report(args, cwd, "auth logout")
}
fn logout_report(args: AuthLogoutArgs, cwd: PathBuf, command: &str) -> Result<Value> {
let session_file = session_config_file(&cwd); let session_file = session_config_file(&cwd);
let existed = session_file.exists(); let existed = session_file.exists();
if existed { if existed {
@ -826,7 +831,7 @@ fn auth_logout_report(args: AuthLogoutArgs, cwd: PathBuf) -> Result<Value> {
.with_context(|| format!("failed to remove {}", session_file.display()))?; .with_context(|| format!("failed to remove {}", session_file.display()))?;
} }
Ok(json!({ Ok(json!({
"command": "auth logout", "command": command,
"requires_confirmation": !args.yes, "requires_confirmation": !args.yes,
"removed_cli_session_file": existed, "removed_cli_session_file": existed,
"node_credentials_untouched": true, "node_credentials_untouched": true,
@ -2589,6 +2594,11 @@ fn run_cli() -> Result<()> {
emit_report(&plan, json_output)?; emit_report(&plan, json_output)?;
} }
} }
Commands::Logout(args) => {
let json_output = args.scope.json;
let report = logout_report(args, std::env::current_dir()?, "logout")?;
emit_report(&report, json_output)?;
}
Commands::Auth { command } => { Commands::Auth { command } => {
let (report, json_output) = match command { let (report, json_output) = match command {
AuthCommands::Status(args) => { AuthCommands::Status(args) => {
@ -5823,6 +5833,7 @@ mod tests {
for args in [ for args in [
&["disasmer", "doctor"][..], &["disasmer", "doctor"][..],
&["disasmer", "auth", "status"], &["disasmer", "auth", "status"],
&["disasmer", "logout", "--yes"],
&["disasmer", "auth", "logout", "--yes"], &["disasmer", "auth", "logout", "--yes"],
&[ &[
"disasmer", "disasmer",
@ -5876,11 +5887,42 @@ mod tests {
} }
} }
#[test]
fn top_level_logout_alias_removes_only_cli_session_state() {
let temp = tempfile::tempdir().unwrap();
let session_file = session_config_file(temp.path());
fs::create_dir_all(session_file.parent().unwrap()).unwrap();
fs::write(&session_file, br#"{"kind":"human","token":"local"}"#).unwrap();
let report = logout_report(
AuthLogoutArgs {
yes: true,
scope: CliScopeArgs {
coordinator: None,
tenant: "tenant".to_owned(),
project: "project".to_owned(),
user: "user".to_owned(),
json: false,
},
},
temp.path().to_path_buf(),
"logout",
)
.unwrap();
assert_eq!(report["command"], "logout");
assert_eq!(report["requires_confirmation"], false);
assert_eq!(report["removed_cli_session_file"], true);
assert_eq!(report["node_credentials_untouched"], true);
assert!(!session_file.exists());
}
#[test] #[test]
fn cli_first_json_mode_parses_for_primary_commands() { fn cli_first_json_mode_parses_for_primary_commands() {
for args in [ for args in [
&["disasmer", "doctor", "--json"][..], &["disasmer", "doctor", "--json"][..],
&["disasmer", "login", "--json"], &["disasmer", "login", "--json"],
&["disasmer", "logout", "--yes", "--json"],
&["disasmer", "auth", "status", "--json"], &["disasmer", "auth", "status", "--json"],
&[ &[
"disasmer", "disasmer",

View file

@ -139,6 +139,7 @@ assert.doesNotMatch(
for (const [name, pattern] of [ for (const [name, pattern] of [
["top-level version metadata", /#\[command\(name = "disasmer", version, arg_required_else_help = true\)\]/], ["top-level version metadata", /#\[command\(name = "disasmer", version, arg_required_else_help = true\)\]/],
["top-level logout command", /enum Commands[\s\S]*Logout\(AuthLogoutArgs\)[\s\S]*Auth \{/],
["human report renderer", /fn human_report\(value: &Value\) -> String/], ["human report renderer", /fn human_report\(value: &Value\) -> String/],
["shared report emitter", /fn emit_report<T: Serialize>\(report: &T, json_output: bool\) -> Result<\(\)>/], ["shared report emitter", /fn emit_report<T: Serialize>\(report: &T, json_output: bool\) -> Result<\(\)>/],
["doctor command", /Doctor\(DoctorArgs\)/], ["doctor command", /Doctor\(DoctorArgs\)/],
@ -178,6 +179,7 @@ for (const [name, pattern] of [
["run coordinator active-process coverage", /fn run_contacts_configured_coordinator_and_reports_active_process_conflicts\(\)/], ["run coordinator active-process coverage", /fn run_contacts_configured_coordinator_and_reports_active_process_conflicts\(\)/],
["CLI error classifier coverage", /fn cli_error_classifier_distinguishes_mvp_failure_categories\(\)/], ["CLI error classifier coverage", /fn cli_error_classifier_distinguishes_mvp_failure_categories\(\)/],
["CLI command exit-code coverage", /fn command_report_exit_code_marks_command_failures_only\(\)/], ["CLI command exit-code coverage", /fn command_report_exit_code_marks_command_failures_only\(\)/],
["CLI top-level logout alias coverage", /fn top_level_logout_alias_removes_only_cli_session_state\(\)/],
["CLI run rejection category coverage", /fn run_rejection_reports_machine_readable_error_category\(\)/], ["CLI run rejection category coverage", /fn run_rejection_reports_machine_readable_error_category\(\)/],
["node attach grant disclosure coverage", /fn node_attach_discloses_dangerous_capability_grants\(\)/], ["node attach grant disclosure coverage", /fn node_attach_discloses_dangerous_capability_grants\(\)/],
["quota local status coverage", /fn quota_status_uses_project_config_and_generic_public_limits\(\)/], ["quota local status coverage", /fn quota_status_uses_project_config_and_generic_public_limits\(\)/],

View file

@ -40,6 +40,10 @@ expect(
"no duplicate work note", "no duplicate work note",
/does not automatically mean new product code, a new feature, or even actual implementation work is required/ /does not automatically mean new product code, a new feature, or even actual implementation work is required/
); );
expect(
"prove existing behavior before implementation",
/prefer proving, documenting, or tightening that existing behavior over duplicating capability or overengineering a parallel website mechanism/
);
expect( expect(
"barebones no CSS", "barebones no CSS",
/barebones functional HTML with no CSS/ /barebones functional HTML with no CSS/
@ -52,6 +56,10 @@ expect(
"billing is not MVP website work", "billing is not MVP website work",
/Do not build billing\/upgrade flows into the minimal website for this MVP/ /Do not build billing\/upgrade flows into the minimal website for this MVP/
); );
expect(
"billing-only surfaces are postponed",
/If a website route, API, database field, control, or acceptance item exists only for billing, paid plans, hosted business operations, broad admin\/moderation, team management, provider setup, secret management, or durable account\/business-process management, postpone it instead of building it for this MVP\./
);
assert.doesNotMatch(source, /community-tier/, "use `community tier`, not `community-tier`"); assert.doesNotMatch(source, /community-tier/, "use `community tier`, not `community-tier`");
const lines = criterionLines(); const lines = criterionLines();