● Increment 5 complete. What landed: #41

Merged
navicore merged 2 commits from increment-5 into main 2026-05-30 01:52:57 +00:00
Owner

Piece: Profile config
File: src/cli/profiles.rs (new)
Notes: CliConfig { default_profile, profiles: HashMap<String, Profile> } +
Profile { server }; loads from $HOME/.config/anz/config.toml. Missing
file
→ empty config (silent). Malformed file → hard error (silent ignore
would
hide misconfig).
────────────────────────────────────────
Piece: Server resolution
File: CliConfig::resolve_server
Notes: Precedence: --server (explicit) > --profile NAME > default_profile
from config > None (local mode). Unknown profile lists the known ones in
the error message.
────────────────────────────────────────
Piece: clap_complete dep
File: Cargo.toml
Notes: Single small dep, just the generator
────────────────────────────────────────
Piece: Global --profile flag
File: src/cli/mod.rs
Notes: Same global = true as --server so it works on any subcommand
────────────────────────────────────────
Piece: Completions { shell } subcommand
File: src/cli/mod.rs
Notes: Uses clap_complete::Shell value-enum (bash / zsh / fish / elvish /
PowerShell)
────────────────────────────────────────
Piece: Dispatch
File: src/main.rs
Notes: Loads CliConfig once at startup, resolves effective server, threads
through Login and build_backend; Completions emits via
clap_complete::generate to stdout

Operator workflow it unlocks:

~/.config/anz/config.toml

default_profile = "homelab"

[profiles.homelab]
server = "https://anz.navicore.tech"

[profiles.work]
server = "https://anz.work.example.com"

Routine admin — no flags needed.

anz realm list
anz user add --realm homelab --username alice --email alice@x

Switch profiles on the fly.

anz --profile work realm list

Always wins.

anz --server https://anz.staging.example.com realm list

Force local (bootstrap / break-glass on the box).

(passing neither --server nor --profile and no default_profile in config)

anz realm create master

Completions.

anz completions zsh > ~/.zfunc/_anz
anz completions bash > /etc/bash_completion.d/anz
anz completions fish > ~/.config/fish/completions/anz.fish

Tests added (8):

  • empty_config_resolves_to_local_mode
  • explicit_server_overrides_everything (over both --profile and
    default_profile)
  • explicit_profile_looks_up_server
  • default_profile_kicks_in_when_no_flag
  • unknown_profile_is_error_with_known_list — error message names the bad
    input and what's available
  • default_profile_pointing_at_missing_is_error — typos in config fail
    loudly
  • missing_file_loads_as_empty
  • malformed_file_is_hard_error

Manually verified: anz completions zsh emits a valid #compdef anz script;
anz completions bash emits a real completion function.

Piece: Profile config File: src/cli/profiles.rs (new) Notes: CliConfig { default_profile, profiles: HashMap<String, Profile> } + Profile { server }; loads from $HOME/.config/anz/config.toml. Missing file → empty config (silent). Malformed file → hard error (silent ignore would hide misconfig). ──────────────────────────────────────── Piece: Server resolution File: CliConfig::resolve_server Notes: Precedence: --server (explicit) > --profile NAME > default_profile from config > None (local mode). Unknown profile lists the known ones in the error message. ──────────────────────────────────────── Piece: clap_complete dep File: Cargo.toml Notes: Single small dep, just the generator ──────────────────────────────────────── Piece: Global --profile flag File: src/cli/mod.rs Notes: Same global = true as --server so it works on any subcommand ──────────────────────────────────────── Piece: Completions { shell } subcommand File: src/cli/mod.rs Notes: Uses clap_complete::Shell value-enum (bash / zsh / fish / elvish / PowerShell) ──────────────────────────────────────── Piece: Dispatch File: src/main.rs Notes: Loads CliConfig once at startup, resolves effective server, threads through Login and build_backend; Completions emits via clap_complete::generate to stdout Operator workflow it unlocks: # ~/.config/anz/config.toml default_profile = "homelab" [profiles.homelab] server = "https://anz.navicore.tech" [profiles.work] server = "https://anz.work.example.com" # Routine admin — no flags needed. anz realm list anz user add --realm homelab --username alice --email alice@x # Switch profiles on the fly. anz --profile work realm list # Always wins. anz --server https://anz.staging.example.com realm list # Force local (bootstrap / break-glass on the box). # (passing neither --server nor --profile and no default_profile in config) anz realm create master # Completions. anz completions zsh > ~/.zfunc/_anz anz completions bash > /etc/bash_completion.d/anz anz completions fish > ~/.config/fish/completions/anz.fish Tests added (8): - empty_config_resolves_to_local_mode - explicit_server_overrides_everything (over both --profile and default_profile) - explicit_profile_looks_up_server - default_profile_kicks_in_when_no_flag - unknown_profile_is_error_with_known_list — error message names the bad input and what's available - default_profile_pointing_at_missing_is_error — typos in config fail loudly - missing_file_loads_as_empty - malformed_file_is_hard_error Manually verified: anz completions zsh emits a valid #compdef anz script; anz completions bash emits a real completion function.
● Increment 5 complete. What landed:
All checks were successful
CI / ci (pull_request) Successful in 4m10s
89832a717c
Piece: Profile config
  File: src/cli/profiles.rs (new)
  Notes: CliConfig { default_profile, profiles: HashMap<String, Profile> } +
    Profile { server }; loads from $HOME/.config/anz/config.toml. Missing
  file
     → empty config (silent). Malformed file → hard error (silent ignore
  would
     hide misconfig).
  ────────────────────────────────────────
  Piece: Server resolution
  File: CliConfig::resolve_server
  Notes: Precedence: --server (explicit) > --profile NAME > default_profile
    from config > None (local mode). Unknown profile lists the known ones in
    the error message.
  ────────────────────────────────────────
  Piece: clap_complete dep
  File: Cargo.toml
  Notes: Single small dep, just the generator
  ────────────────────────────────────────
  Piece: Global --profile flag
  File: src/cli/mod.rs
  Notes: Same global = true as --server so it works on any subcommand
  ────────────────────────────────────────
  Piece: Completions { shell } subcommand
  File: src/cli/mod.rs
  Notes: Uses clap_complete::Shell value-enum (bash / zsh / fish / elvish /
    PowerShell)
  ────────────────────────────────────────
  Piece: Dispatch
  File: src/main.rs
  Notes: Loads CliConfig once at startup, resolves effective server, threads
    through Login and build_backend; Completions emits via
    clap_complete::generate to stdout

  Operator workflow it unlocks:

  # ~/.config/anz/config.toml
  default_profile = "homelab"

  [profiles.homelab]
  server = "https://anz.navicore.tech"

  [profiles.work]
  server = "https://anz.work.example.com"

  # Routine admin — no flags needed.
  anz realm list
  anz user add --realm homelab --username alice --email alice@x

  # Switch profiles on the fly.
  anz --profile work realm list

  # Always wins.
  anz --server https://anz.staging.example.com realm list

  # Force local (bootstrap / break-glass on the box).
  # (passing neither --server nor --profile and no default_profile in config)
  anz realm create master

  # Completions.
  anz completions zsh  > ~/.zfunc/_anz
  anz completions bash > /etc/bash_completion.d/anz
  anz completions fish > ~/.config/fish/completions/anz.fish

  Tests added (8):
  - empty_config_resolves_to_local_mode
  - explicit_server_overrides_everything (over both --profile and
  default_profile)
  - explicit_profile_looks_up_server
  - default_profile_kicks_in_when_no_flag
  - unknown_profile_is_error_with_known_list — error message names the bad
  input and what's available
  - default_profile_pointing_at_missing_is_error — typos in config fail
  loudly
  - missing_file_loads_as_empty
  - malformed_file_is_hard_error

  Manually verified: anz completions zsh emits a valid #compdef anz script;
  anz completions bash emits a real completion function.
Author
Owner

Review: Increment 5 — profiles + shell completions

Verdict: clean and nearly ready — one coupling bug worth fixing before merge (details below). Lowest-risk increment in the series: pure CLI ergonomics, no server surface. Verified locally: clippy clean with -D warnings, 165 tests pass, and anz completions {zsh,bash} emit valid scripts (#compdef anz, a real bash function, with --profile present as a global). Nice to see the prior round's feedback already landed in the base — insecure_server_classification and the dead-bail removal.

Verified

  • profiles.rs is well-built. Precedence --server > --profile > default_profile > local matches the doc and is exhaustively tested (8 cases). The missing-file→empty vs malformed-file→hard-error distinction is the right call. Unknown-profile and typo'd-default_profile both fail loudly and name the bad input + known profiles.
  • completions is pure — generates to stdout via clap_complete, no DB/login/state needed, returns early. Confirmed the --profile/--server globals show up on subcommands.
  • Login now benefits from profiles tooanz login --profile work (or a default_profile) resolves a server, an improvement over Increment 4 where login required an explicit --server.

Worth fixing before merge

Profile resolution is coupled to commands that don't use it. CliConfig::load_or_default()? and resolve_server()? both run before the command match, so their errors abort every command — including serve, completions, and logout, none of which consult profiles. Reproduced with a plausible operator typo (default_profile = "homelab" but no [profiles.homelab]):

$ anz completions zsh
Error: default_profile='homelab' not found in config; known profiles: <none>   (exit 1)

$ anz serve
Error: default_profile='homelab' not found in config; known profiles: <none>   (exit 1)

Two distinct problems:

  • completions failing is the worst of these — completion scripts are sourced during shell init, so an unrelated profile typo (or a malformed config.toml) silently breaks tab-completion for the whole shell. Completions should generate unconditionally.
  • serve is the on-box daemon and shouldn't depend on the desktop CLI's profile config being well-formed at all — its startup is now gated on a file it never reads.

Fix is small: resolve the server lazily, only inside the branches that need it (the admin commands + login), and leave serve/completions/logout off that path. That also defers the malformed-file hard error to where it's relevant.

Minor

  • --server + --profile together silently lets --server win (documented + tested). Fine as-is; a one-line "note: --profile ignored because --server is set" would be friendlier but isn't necessary.
  • A profile's server URL gets no well-formedness/scheme check at resolution time, but it flows into the same insecure_server_classification path that --server does, so there's no new gap — just confirming it's consistent.
## Review: Increment 5 — profiles + shell completions **Verdict: clean and nearly ready** — one coupling bug worth fixing before merge (details below). Lowest-risk increment in the series: pure CLI ergonomics, no server surface. Verified locally: clippy clean with `-D warnings`, 165 tests pass, and `anz completions {zsh,bash}` emit valid scripts (`#compdef anz`, a real bash function, with `--profile` present as a global). Nice to see the prior round's feedback already landed in the base — `insecure_server_classification` and the dead-`bail` removal. ### Verified - **`profiles.rs` is well-built.** Precedence `--server > --profile > default_profile > local` matches the doc and is exhaustively tested (8 cases). The missing-file→empty vs malformed-file→hard-error distinction is the right call. Unknown-profile and typo'd-`default_profile` both fail loudly and name the bad input + known profiles. - **`completions` is pure** — generates to stdout via `clap_complete`, no DB/login/state needed, returns early. Confirmed the `--profile`/`--server` globals show up on subcommands. - **Login now benefits from profiles too** — `anz login --profile work` (or a `default_profile`) resolves a server, an improvement over Increment 4 where login required an explicit `--server`. ### Worth fixing before merge **Profile resolution is coupled to commands that don't use it.** `CliConfig::load_or_default()?` and `resolve_server()?` both run *before* the command `match`, so their errors abort every command — including `serve`, `completions`, and `logout`, none of which consult profiles. Reproduced with a plausible operator typo (`default_profile = "homelab"` but no `[profiles.homelab]`): ``` $ anz completions zsh Error: default_profile='homelab' not found in config; known profiles: <none> (exit 1) $ anz serve Error: default_profile='homelab' not found in config; known profiles: <none> (exit 1) ``` Two distinct problems: - **`completions` failing is the worst of these** — completion scripts are sourced during shell init, so an unrelated profile typo (or a malformed `config.toml`) silently breaks tab-completion for the whole shell. Completions should generate unconditionally. - **`serve` is the on-box daemon** and shouldn't depend on the desktop CLI's profile config being well-formed at all — its startup is now gated on a file it never reads. Fix is small: resolve the server lazily, only inside the branches that need it (the admin commands + `login`), and leave `serve`/`completions`/`logout` off that path. That also defers the malformed-file hard error to where it's relevant. ### Minor - `--server` + `--profile` together silently lets `--server` win (documented + tested). Fine as-is; a one-line "note: --profile ignored because --server is set" would be friendlier but isn't necessary. - A profile's `server` URL gets no well-formedness/scheme check at resolution time, but it flows into the same `insecure_server_classification` path that `--server` does, so there's no new gap — just confirming it's consistent.
● Behaves exactly as the reviewer specified:
All checks were successful
CI / ci (pull_request) Successful in 4m3s
cc1be4b2ed
┌──────────────────┬─────────────────────┬────────────────────────────┐
  │     Command      │     Before fix      │         After fix          │
  ├──────────────────┼─────────────────────┼────────────────────────────┤
  │ anz completions  │ Errored — silently  │ Succeeds — emits the       │
  │ zsh (with broken │ kills shell-init    │ #compdef anz script        │
  │  config)         │ tab-completion      │ (rustup's noise is         │
  │                  │                     │ unrelated)                 │
  ├──────────────────┼─────────────────────┼────────────────────────────┤
  │                  │                     │ Succeeds —                 │
  │ anz logout       │ Errored             │ token-cache-only, profile  │
  │                  │                     │ config ignored             │
  ├──────────────────┼─────────────────────┼────────────────────────────┤
  │                  │ Errored — on-box    │ Would succeed (same code   │
  │ anz serve        │ daemon depending on │ path as logout — no        │
  │                  │  desktop config     │ profile resolution)        │
  ├──────────────────┼─────────────────────┼────────────────────────────┤
  │ anz realm list   │                     │ Still errors with the same │
  │ (a command that  │ Errored with the    │  actionable message —      │
  │ does need a      │ same message        │ operator sees the typo     │
  │ server)          │                     │ immediately                │
  └──────────────────┴─────────────────────┴────────────────────────────┘

  Summary of the response:

  Single fix, structural — extracted resolve_remote_server(server_flag,
  profile_flag) as a lazy helper, wired into only the branches that need a
  server (Login, build_backend). Serve, Logout, Completions no longer touch
  profile config. The malformed-config hard-error is preserved exactly where
  it's relevant — admin commands that would otherwise silently fall back to
  local mode.

  Side-effect of the refactor: passing flag scalars (Option<&str>) into the
  helpers instead of &Cli cleaned up a partial-move issue too (the Login {
  realm } arm consumes the realm String, which made &cli invalid downstream —
   unrelated to the reviewer's note but cleaner anyway).

  165 tests still pass, clippy clean. The reviewer's minor (--server silently
   winning over --profile) deliberately left as documented behavior.
navicore deleted branch increment-5 2026-05-30 01:52:58 +00:00
Sign in to join this conversation.
No description provided.