Skip to content

feat: add session-level profile selection and identity diagnostics#1776

Open
luozhixiong01 wants to merge 29 commits into
mainfrom
feat/session-profile-selection
Open

feat: add session-level profile selection and identity diagnostics#1776
luozhixiong01 wants to merge 29 commits into
mainfrom
feat/session-profile-selection

Conversation

@luozhixiong01

@luozhixiong01 luozhixiong01 commented Jul 7, 2026

Copy link
Copy Markdown
Collaborator

Summary

Add session-level profile selection and make credential arbitration explicit and diagnosable. A selected profile can safely coexist with matching direct-credential environment variables, while incomplete credentials, conflicts, invalid policy values, missing profiles, and malformed configuration retain precise typed errors.

Changes

  • Add LARKSUITE_CLI_PROFILE selection with --profile precedence in cmd/bootstrap.go and expose the effective source through whoami --json.
  • Refactor internal/credential/credential_provider.go into explicit gather, decide, and execute stages so profile, direct environment credentials, managed providers, and config defaults use one decision table.
  • Add typed credential block metadata and structured repair fields in extension/credential/types.go, errs/types.go, and errs/subtypes.go, preserving safe causes without exposing secrets or tokens.
  • Validate LARKSUITE_CLI_DEFAULT_AS and LARKSUITE_CLI_STRICT_MODE before credential completeness checks in extension/credential/env/env.go.
  • Keep profile credentials and tokens authoritative when the selected profile app ID matches direct environment credentials; return a typed conflict when app IDs differ.
  • Distinguish effective invocation identity from the saved default in cmd/whoami, cmd/config, and cmd/profile; profile list --json now reports default instead of active.
  • Add decision-table, provider, command, error-envelope, secret-redaction, and profile-selection coverage across the affected test packages.
  • Update skills/lark-shared/SKILL.md with profile-selection routing guidance validated by the skill evaluation suite.

Test Plan

  • make unit-test, go vet ./..., gofmt -l ., go mod tidy, and golangci-lint run --new-from-rev=origin/main passed
  • validate passed
  • local-eval passed (E2E 4/4, skillave 7/7)
  • acceptance-reviewer passed (5/5 cases)
  • security review passed (0 findings)
  • manual verification: LARKSUITE_CLI_PROFILE=tenant_b ./lark-cli whoami --json --as bot and the isolated matching/conflict/UAT-only matrix passed with a freshly built binary

Related Issues

N/A

@luozhixiong01 luozhixiong01 added size/M Single-domain feat or fix with limited business impact feature labels Jul 7, 2026
@github-actions github-actions Bot added size/L Large or sensitive change across domains or core paths and removed size/M Single-domain feat or fix with limited business impact labels Jul 7, 2026
@coderabbitai

coderabbitai Bot commented Jul 7, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR adds profile-aware credential selection: bootstrap resolves the active profile from a flag or LARKSUITE_CLI_PROFILE, credential resolution records selection provenance and typed error metadata, whoami exposes that selection in JSON, and related help text/docs are updated.

Changes

Profile-aware credential selection

Layer / File(s) Summary
Profile resolution and wiring
cmd/bootstrap.go, cmd/bootstrap_test.go, internal/cmdutil/factory.go, internal/cmdutil/factory_default.go
BootstrapInvocationContext now resolves Profile from --profile or LARKSUITE_CLI_PROFILE, exposes ProfileFromFlag, and passes that flag into credential provider construction.
Selection source types
internal/credential/identity_selection.go, internal/credential/identity_selection_test.go, internal/envvars/envvars.go
Adds credential source kind constants, DirectCredentialEnv and IdentitySelection structs, Explicit() logic, and the CliProfile env var constant.
Credential arbitration and selection tests
internal/credential/credential_provider.go, internal/credential/credential_provider_selection_test.go
CredentialProvider now caches selection state and arbitrates between extension providers, direct env credentials, and explicit profiles while emitting typed config/validation errors; the selection test suite covers the full decision matrix and secret-leak checks.
Typed selection errors
errs/subtypes.go, errs/types.go, errs/types_test.go, errs/marshal_test.go
Adds config/validation subtypes and new typed-error fields/builders for profile selection conflicts and credential metadata, with JSON marshal and unit coverage.
Whoami credential-source output
cmd/whoami/whoami.go, cmd/whoami/whoami_test.go
whoami reads cached selection state, emits credentialSource, explicit, and directCredentialEnv in JSON, and adds regression coverage for the new fields.
Command help and shared guidance
cmd/auth/status.go, cmd/auth/status_test.go, cmd/profile/profile.go, cmd/profile/list.go, cmd/profile/profile_test.go, cmd/config/show.go, cmd/config/config_test.go, skills/lark-shared/SKILL.md
Updates auth/profile/config help text and tests, plus the shared skill doc’s profile-selection section.

Estimated code review effort: 4 (Complex) | ~60 minutes

Possibly related PRs

Suggested labels: size/L, feature

Suggested reviewers: liangshuo-1, MaxHuang22

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly summarizes the main change: session-level profile selection and identity diagnostics.
Description check ✅ Passed The description follows the required template with Summary, Changes, Test Plan, and Related Issues sections.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/session-profile-selection

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@codecov

codecov Bot commented Jul 7, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 97.42857% with 9 lines in your changes missing coverage. Please review.
✅ Project coverage is 74.82%. Comparing base (37d490a) to head (00d8c07).
⚠️ Report is 2 commits behind head on main.

Files with missing lines Patch % Lines
internal/credential/credential_provider.go 96.55% 5 Missing and 2 partials ⚠️
internal/cmdutil/factory_default.go 60.00% 1 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1776      +/-   ##
==========================================
+ Coverage   74.66%   74.82%   +0.15%     
==========================================
  Files         877      879       +2     
  Lines       91731    92051     +320     
==========================================
+ Hits        68494    68875     +381     
+ Misses      17926    17856      -70     
- Partials     5311     5320       +9     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@github-actions

github-actions Bot commented Jul 7, 2026

Copy link
Copy Markdown

🚀 PR Preview Install Guide

🧰 CLI update

npm i -g https://pkg.pr.new/larksuite/cli/@larksuite/cli@00d8c07de9db0d873b0ec3cfce5de4d6f49589f9

🧩 Skill update

npx skills add larksuite/cli#feat/session-profile-selection -y -g

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (2)
cmd/whoami/whoami_test.go (1)

338-374: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Use cmdutil.TestFactory here. profileSelectionFactory can take the shared factory setup, then override f.Credential with the custom profile-aware provider. That keeps the test aligned with the rest of the suite and avoids duplicating boilerplate in this helper.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cmd/whoami/whoami_test.go` around lines 338 - 374, `profileSelectionFactory`
is hand-building a `cmdutil.Factory` instead of using the shared test helper.
Switch this helper to start from `cmdutil.TestFactory` and then override the
returned factory’s `Credential` with the custom
`credential.NewCredentialProvider`/`WithProfile` setup, while keeping the
existing config and IO stream tweaks. This keeps the test consistent with the
rest of the suite and removes duplicated factory boilerplate.

Source: Path instructions

internal/credential/credential_provider.go (1)

233-262: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicate enrichment/warning logic — extract a helper.

The account-enrichment + warn-and-clear-on-failure block here (Lines 245-254) is duplicated almost verbatim at Lines 320-329 for the env-direct path. Consider extracting a shared helper (e.g. p.enrichAndClearOnFailure(ctx, acct, source)) to avoid future divergence.

♻️ Proposed refactor
+func (p *CredentialProvider) enrichOrClear(ctx context.Context, acct *Account, source credentialSource) {
+	if err := p.enrichUserInfo(ctx, acct, source); err != nil {
+		if p.warnOut != nil {
+			_, _ = fmt.Fprintf(p.warnOut, "warning: unable to verify user identity from credential source %q: %v\n", source.Name(), err)
+		}
+		acct.UserOpenId = ""
+		acct.UserName = ""
+	}
+}

Then replace both call sites (Lines 245-254 and 320-329) with p.enrichOrClear(ctx, internal, source) / p.enrichOrClear(ctx, envAcct, envSource).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/credential/credential_provider.go` around lines 233 - 262, The
account enrichment and warning/clear-on-failure logic in credential_provider.go
is duplicated between the non-env provider path and the env-direct path. Extract
that shared behavior from the credential selection flow in CredentialProvider
into a helper (for example, on p as enrichOrClear or enrichAndClearOnFailure)
that takes ctx, the converted account, and the source, calls enrichUserInfo,
logs the warning through warnOut, and clears UserOpenId/UserName on failure.
Then replace both existing call sites in the provider-selection logic with the
helper so the two paths stay consistent.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@cmd/bootstrap.go`:
- Around line 31-39: The profile bootstrap logic is using globals.Profile != ""
to detect whether --profile was provided, which incorrectly treats an explicit
empty value the same as no flag. Update the bootstrap path in the command setup
to use fs.Changed("profile") to determine whether the user passed the flag, and
only fall back to envvars.CliProfile when the flag was not set. Keep the
InvocationContext construction in sync so ProfileFromFlag reflects the explicit
flag state rather than the resolved value.

In `@internal/credential/credential_provider.go`:
- Around line 264-277: The explicit-profile branch in credential_provider.go is
swallowing the error from core.LoadMultiAppConfig and always turning it into
profile_not_found, which masks real config-loading failures. Update the logic
around p.profile, LoadMultiAppConfig, and the app == nil check so config load
errors are preserved and propagated instead of reported as a missing profile;
only return SubtypeProfileNotFound when the config loads successfully but
FindApp(p.profile) returns nil. Follow the pattern used in the later no-profile
branch by inspecting errs.ProblemOf(loadErr) when applicable and attaching the
original error with .WithCause(loadErr) so errors.Is and errors.Unwrap keep
working.

---

Nitpick comments:
In `@cmd/whoami/whoami_test.go`:
- Around line 338-374: `profileSelectionFactory` is hand-building a
`cmdutil.Factory` instead of using the shared test helper. Switch this helper to
start from `cmdutil.TestFactory` and then override the returned factory’s
`Credential` with the custom `credential.NewCredentialProvider`/`WithProfile`
setup, while keeping the existing config and IO stream tweaks. This keeps the
test consistent with the rest of the suite and removes duplicated factory
boilerplate.

In `@internal/credential/credential_provider.go`:
- Around line 233-262: The account enrichment and warning/clear-on-failure logic
in credential_provider.go is duplicated between the non-env provider path and
the env-direct path. Extract that shared behavior from the credential selection
flow in CredentialProvider into a helper (for example, on p as enrichOrClear or
enrichAndClearOnFailure) that takes ctx, the converted account, and the source,
calls enrichUserInfo, logs the warning through warnOut, and clears
UserOpenId/UserName on failure. Then replace both existing call sites in the
provider-selection logic with the helper so the two paths stay consistent.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: e5965ff7-ac70-4f0e-ae37-05d2790a35c5

📥 Commits

Reviewing files that changed from the base of the PR and between f0b6f35 and 251de0f.

📒 Files selected for processing (20)
  • cmd/auth/status.go
  • cmd/auth/status_test.go
  • cmd/bootstrap.go
  • cmd/bootstrap_test.go
  • cmd/profile/profile.go
  • cmd/profile/profile_test.go
  • cmd/whoami/whoami.go
  • cmd/whoami/whoami_test.go
  • errs/marshal_test.go
  • errs/subtypes.go
  • errs/types.go
  • errs/types_test.go
  • internal/cmdutil/factory.go
  • internal/cmdutil/factory_default.go
  • internal/credential/credential_provider.go
  • internal/credential/credential_provider_selection_test.go
  • internal/credential/identity_selection.go
  • internal/credential/identity_selection_test.go
  • internal/envvars/envvars.go
  • skills/lark-shared/SKILL.md

Comment thread cmd/bootstrap.go
Comment thread internal/credential/credential_provider.go Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
skills/lark-shared/SKILL.md (1)

129-132: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Language inconsistency in new "Profile 选择" section.

The section header is Chinese but the content on Line 131 is entirely English, unlike every other section in this document (auth table, update-check, security rules) which is written in Chinese. Consider translating this content for consistency with the rest of the skill guidance.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@skills/lark-shared/SKILL.md` around lines 129 - 132, The new “Profile 选择”
section is inconsistent because its guidance text is written in English while
the surrounding skill documentation uses Chinese. Update the content in this
section to Chinese for consistency, keeping the same meaning for profile
selection, CLI flags, environment variables, saved config, default profile
handling, and the ambiguity/credential note; use the existing “Profile 选择”
heading and nearby sections as the style reference.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@skills/lark-shared/SKILL.md`:
- Around line 129-132: The new “Profile 选择” section is inconsistent because its
guidance text is written in English while the surrounding skill documentation
uses Chinese. Update the content in this section to Chinese for consistency,
keeping the same meaning for profile selection, CLI flags, environment
variables, saved config, default profile handling, and the ambiguity/credential
note; use the existing “Profile 选择” heading and nearby sections as the style
reference.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 4a654e00-7609-4bc0-9a4f-ac3211765632

📥 Commits

Reviewing files that changed from the base of the PR and between 425122e and d44c70f.

📒 Files selected for processing (6)
  • cmd/config/config_test.go
  • cmd/config/show.go
  • cmd/profile/list.go
  • cmd/profile/profile.go
  • cmd/profile/profile_test.go
  • skills/lark-shared/SKILL.md
✅ Files skipped from review due to trivial changes (1)
  • cmd/config/show.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • cmd/profile/profile.go

@luozhixiong01

Copy link
Copy Markdown
Collaborator Author

Language inconsistency in "Profile 选择" section

Addressed in 94032da — the ## Profile 选择 section is now written in Chinese to match the rest of this skill document (command and env-var tokens kept in English). Thanks for the catch.

@luozhixiong01 luozhixiong01 force-pushed the feat/session-profile-selection branch from 8b26c71 to 20571e0 Compare July 13, 2026 06:22
Add LARKSUITE_CLI_PROFILE env var and make BootstrapInvocationContext
fall back to it when --profile is empty, so downstream credential
resolution sees the correct profile. Also track whether the resolved
profile came from the flag or the env fallback via a new
InvocationContext.ProfileFromFlag field, needed by a later task to
report the correct credential source.
Declares the 5 stable error subtypes (4 config + 1 validation) and the
ConfigError/ValidationError extension fields the profile-selection
credential core (Task 4) will produce, plus builder-chain and wire-pin
tests pinning their shape.
…env provider

Mirror the env-incomplete block-path guard on the success-account path so a
non-env extension provider (e.g. sidecar, Priority 0) that returns an account
wins outright instead of being misreported as a direct-credential env account.
This restores pre-diff behavior for such providers: no profile arbitration, no
spurious profile_app_credential_conflict, and DirectCredentialEnv.Present stays
false when no direct env vars are set. Env matrix states are unchanged.

Add TestSelection_NonEnvExtensionProviderWinsOverProfile as a regression guard.
…cause

Add a case where the underlying account-resolution error itself contains a
secret marker, proving doResolveAccount's drop-the-cause design (§5.1) holds
beyond the existing noop-keychain (empty-error) test, including across the
full errors.Unwrap chain.
whoami reports facts about the effective identity; it should not
proactively push profile-switching guidance at agents. That guidance
lives in `profile --help` / the lark-shared skill, and failure recovery
already lives in error hints. Remove the now-unused Suggestion field
from IdentitySelection and its only setter/consumer.
…ofile_secret_invalid for broken default secret
Replace credential-shaped literals in whoami and selection tests with
placeholder values recognized by the public-content quality gate
(test-secret / your-secret / your-password / your-access-token), so the
deterministic public-content scan does not flag test fixtures as generic
credentials. No behavioral change; the fixtures are only compared for
non-leakage and identity arbitration.
Clarify that --profile and LARKSUITE_CLI_PROFILE accept either a profile
name or an app_id, keep the effective-identity vs OAuth-token boundary
(whoami vs auth status --json --verify), and note not to set direct
app-credential env vars unless direct credentials are provided.
When an explicit profile was requested and LoadMultiAppConfig failed, the
error was discarded and every failure reported as profile_not_found,
masking a real config problem (e.g. malformed file) behind a misleading
"run profile list" hint. Propagate the underlying error when it is a
malformed-config failure (errors.Is ErrMalformedConfig) so errors.Is /
errors.Unwrap keep working, mirroring the no-profile branch. An absent
config is not malformed and still yields the friendly profile_not_found.
profile list / config show only report the saved default profile, not the
app/profile a specific invocation actually resolves to (especially under
--profile or LARKSUITE_CLI_PROFILE). Rename the misleading profile list
JSON field active -> default (it is the configured default, not the one in
effect), and point config show / profile list / profile help at
lark-cli whoami --json for the identity actually used now. Restructure the
lark-shared skill profile guidance as an intent -> command table.
Match the rest of the skill document, which is in Chinese. Keep command
and env-var tokens in English. Content unchanged.
The config-default (no-profile) path loaded config via LoadMultiAppConfig
and coerced any error into NotConfiguredError, so a malformed config was
misreported as no_active_profile ("run config init") — risking overwrite
of a recoverable-but-broken config. Route both the explicit-profile and
config-default paths through core.LoadOrNotConfigured so a malformed config
consistently surfaces as invalid_config (exit 3, cause preserved), while a
genuinely absent config keeps the friendly profile_not_found / no_active_profile.
@luozhixiong01 luozhixiong01 force-pushed the feat/session-profile-selection branch from 20571e0 to e6f8b8a Compare July 14, 2026 08:55
The description broadening to profile-selection triggers exceeded the
routing fix's actual scope: the observed failure was command selection
after the skill was already engaged, not skill discovery. Routing
guidance stays in the body's profile-selection section and command
help; the frontmatter matches main again.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature size/L Large or sensitive change across domains or core paths

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant