Skip to content

Phase 3 follow-up: realm picker + pickable-option help annotations - #785

Merged
vscheuber merged 2 commits into
mainfrom
feat/phase3-followup
Oct 11, 2026
Merged

vscheuber merged 2 commits into
mainfrom
feat/phase3-followup

Conversation

@vscheuber

Copy link
Copy Markdown
Contributor

Phase 3 follow-up: realm picker + pickable-option help annotations

Completes the last two Phase 3 items from the interactive-commands plan (builds on #784).

Realm picker

After a successful login, a command whose realm argument was left at its default offers a dropdown of the target system's realms — but only when it makes sense to:

  • prompting is allowed (canPrompt() — never in FRODO_TEST/CI or non-TTY runs),
  • the command declares a [realm] positional argument,
  • the user did not name a realm (positional or FRODO_REALM), and
  • the target system reports more than one realm (a single-realm tenant has no choice to offer).

Every other path is a no-op, so non-interactive behavior is byte-identical to before. Escape declines the offer and keeps the deployment-type default (unlike entity pickers, there is no "wrong" continuation to protect — the default is a valid choice). A failed realm listing is swallowed and the default kept.

The two subtle parts:

  • Provenance is captured pre-auth. frodo-lib maps the __default__realm__ sentinel to the deployment type's realm (alpha / /) during _getTokens, so after auth a user choice and a mapped default are indistinguishable. noteRealmProvenance() therefore runs in the resolution pass before auth; the picker consults it post-auth.
  • One fetch for the realms endpoint. The picker and frodo realm list now share a memoized readRealmsCached() in RealmOps — without it, the picker's extra read broke Polly's order-matched replay in existing e2e recordings (they hold one entry for the endpoint) and doubled latency.

Wiring is global: the getTokens wrapper fires the pick for every command; commands that bypass the resolution pass (login/conn stub actions) naturally never see it.

Help annotation for pickable options

FrodoStubHelp now renders (pick from <kind>) on options with a registered entity picker, e.g.:

-i, --secret-id <secret-id>  Secret id. If specified, -a is ignored. (pick from secret)

so interactive users can see which options will offer a live dropdown before running the command. The 25 affected client_cli help snapshots are updated (annotation-only diffs, verified).

Layering note

The picker's post-auth hook needs the active command, but ops/ importing
cli/ResolveInteractiveInputs directly would drag the resolution pass's
graph (the frodo-lib-importing interactive prompts) into tests that mock
frodo-lib wholesale. The active-command registry therefore lives in its own
zero-import module (ActiveActionCommandState, same split as
PromptGateState), and the picker machinery is lazy-imported in the
getTokens wrapper behind canPrompt() — non-interactive sessions never
load it.

Tests

  • RealmPickers.test.ts (new): pins the non-interactive preservation contract — under FRODO_TEST=1, without a realm argument, with a user-specified realm, or without provenance noted, resolveRealmPick is a strict no-op (no read, no write).
  • Full suite green locally against the fresh build; all 362 client_cli suites pass (snapshot updates applied); PTY-verified against the rebuilt binary (picker renders for the 2-realm mock tenant; default accepted and explicit pick both complete cleanly).

🤖 Generated with Claude Code

vscheuber and others added 2 commits October 11, 2026 00:07
Completes the last two Phase 3 items of the interactive-commands plan.

Realm picker: after successful auth, a command whose realm argument was
left at its default offers a dropdown of the target system's realms --
only when prompting is allowed, the command declares a realm argument,
the user did not name one (provenance captured pre-auth, since frodo-lib
maps the __default__realm__ sentinel to the deployment type's realm
during auth), and the system has more than one realm. Everything else is
a no-op; escape keeps the default. The picker and `frodo realm list`
share a memoized readRealmsCached() in RealmOps so the realms endpoint is
fetched once per invocation (keeps Polly's order-matched e2e replay
intact and halves latency).

Help annotations: FrodoStubHelp renders "(pick from <kind>)" on options
with a registered entity picker, so interactive users can see which
options will offer a live dropdown. 25 client_cli help snapshots updated
(annotation-only diffs).

Layering: the active-command registry lives in zero-import
ActiveActionCommandState (PromptGateState pattern) and the picker
machinery is lazy-imported behind canPrompt(), so AuthenticateOps stays
loadable in tests that mock frodo-lib wholesale.

Co-Authored-By: Claude Code <noreply@anthropic.com>
The Phase 1b bare-stub sub-command picker replaces commander's
parseOptions wholesale, so `frodo -v` and `frodo -h` (and the -hh/-hhh
help levels) on a TTY showed the picker instead of their own output --
the interception never reached the option events that make commander
print and exit. `frodo --no-prompt` was likewise swallowed, defeating
the flag's meaning before any leaf preAction could honor it.

The interception now falls through to commander's own parse whenever
unknown argv carries one of those tokens (BARE_PICKER_BLOCKING_OPTION_
TOKENS); pass-down options (--verbose, --debug, ...) keep the picker and
re-parse in the picked sub-command's context as designed.

Regression tests: test/unit/bare-stub-root-flags.pty.test.js via the
ShellPty PTY harness with a new allowPrompting opt-in (blanks FRODO_TEST
explicitly -- the driver merges the runner's environment under the
scenario env, so an omitted key would leak the runner's value). Verified
to fail against the pre-fix build and pass against the fixed one.

Co-Authored-By: Claude Code <noreply@anthropic.com>
@vscheuber
vscheuber merged commit 9a5d5fd into main Oct 11, 2026
13 checks passed
@vscheuber
vscheuber deleted the feat/phase3-followup branch October 11, 2026 14:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant