Skip to content

fix(auth): make agent authorization resumable#1867

Closed
irinakk wants to merge 1 commit into
larksuite:mainfrom
irinakk:codex/auth-loop
Closed

fix(auth): make agent authorization resumable#1867
irinakk wants to merge 1 commit into
larksuite:mainfrom
irinakk:codex/auth-loop

Conversation

@irinakk

@irinakk irinakk commented Jul 13, 2026

Copy link
Copy Markdown

Summary

  • persist the latest pending device authorization flow per app when auth login --no-wait returns
  • add auth login --resume so a later agent turn can continue an unexpired flow without carrying the device code in conversation context
  • reject config init in managed environments unless --force-init is explicitly provided
  • update the shared auth skill to use resumable split-flow authorization and avoid background polling jobs

Why

Agent-hosted sessions can end while a device authorization command is still pending. Requiring the next turn to recover a device code from conversation context makes the flow fragile and can lead to repeated authorization links. Managed hosts also need a clear guard against creating a second app configuration when provisioning is temporarily unavailable.

Test plan

  • GOTOOLCHAIN=go1.24.3 make unit-test
  • GOTOOLCHAIN=go1.24.3 go vet ./...
  • gofmt -l .
  • GOTOOLCHAIN=go1.24.3 go mod tidy with no go.mod / go.sum diff
  • golangci-lint v2.1.6 run --new-from-rev=origin/main

Summary by CodeRabbit

  • New Features

    • Added a two-step authentication flow using --no-wait --json, followed later by --resume.
    • Improved guidance for QR-code authorization and displaying verification links.
    • Added safeguards for managed profiles during configuration initialization, with --force-init available as an override.
  • Bug Fixes

    • Pending authorization sessions now expire, remain isolated by application, and clean up correctly.
    • Updated authentication help and error messages to reflect the new resume flow.

@github-actions github-actions Bot added the size/L Large or sensitive change across domains or core paths label Jul 13, 2026
@coderabbitai

coderabbitai Bot commented Jul 13, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The CLI adds auth login --resume for completing pending device authorization saved by --no-wait, with expiration and app-scoped cache handling. Auth guidance and tests now describe the split flow. config init also rejects managed profiles unless forced.

Changes

Authentication resume flow

Layer / File(s) Summary
Pending login persistence
cmd/auth/login_scope_cache.go, cmd/auth/login_scope_cache_test.go
Pending authorization records are saved per app with device code, requested scope, and expiration; loading and cleanup validate and remove stale or mismatched records.
Resume command execution
cmd/auth/login.go, cmd/auth/login_test.go
auth login --resume validates options, loads the latest pending authorization, and uses it for polling and cleanup.
Split-flow guidance and message contracts
cmd/auth/auth_test.go, cmd/auth/login.go, cmd/auth/login_messages.go, cmd/auth/login_messages_test.go, cmd/auth/login_test.go, skills/lark-shared/SKILL.md
Help text, agent hints, timeout messages, skill instructions, and assertions now direct later authorization completion through --resume and specify QR display behavior.

Managed profile initialization

Layer / File(s) Summary
Managed profile guard and guidance
cmd/config/init.go, cmd/config/init_guard_test.go, skills/lark-shared/SKILL.md
config init returns a not-configured error for managed profiles unless --force-init is used, with tests covering local, refused, and forced cases; skill guidance documents the rule.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Agent
  participant AuthLogin
  participant PendingLoginCache
  participant DeviceAuthorization
  Agent->>AuthLogin: Run --no-wait --json
  AuthLogin->>PendingLoginCache: Save pending authorization
  Agent->>AuthLogin: Run --resume later
  AuthLogin->>PendingLoginCache: Load latest valid authorization
  AuthLogin->>DeviceAuthorization: Poll with saved device code
  DeviceAuthorization-->>AuthLogin: Return authorization result
Loading

Possibly related PRs

Suggested labels: documentation

Suggested reviewers: jackzhao10086, maxhuang22, albertnusouo, xingjiansun

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 6.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and clearly summarizes the main change: making agent authorization resumable.
Description check ✅ Passed The description covers summary, rationale, and test plan, but it omits the template's Changes and Related Issues sections.
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.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@irinakk

irinakk commented Jul 13, 2026

Copy link
Copy Markdown
Author

Closing for now; this public PR was opened prematurely before the intended review and coordination.

@irinakk irinakk closed this Jul 13, 2026

@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: 6

🧹 Nitpick comments (1)
cmd/auth/login_scope_cache_test.go (1)

98-102: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use standard-library APIs for test fixture setup.

Creating fixtures through vfs makes this test circular because it exercises the same abstraction used by production. Use os.MkdirAll and os.WriteFile; retaining vfs.Stat for the assertion is fine.

Based on learnings, test fixtures under t.TempDir() must use standard-library filesystem APIs rather than vfs.

Proposed fix
-	if err := vfs.MkdirAll(loginScopeCacheDir(), 0700); err != nil {
+	if err := os.MkdirAll(loginScopeCacheDir(), 0700); err != nil {
 		t.Fatalf("MkdirAll() error = %v", err)
 	}
-	if err := vfs.WriteFile(pendingLoginPath(record.AppID), data, 0600); err != nil {
+	if err := os.WriteFile(pendingLoginPath(record.AppID), data, 0600); err != nil {
🤖 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/auth/login_scope_cache_test.go` around lines 98 - 102, Update the fixture
setup around loginScopeCacheDir and pendingLoginPath to use os.MkdirAll and
os.WriteFile instead of vfs.MkdirAll and vfs.WriteFile, preserving the existing
permissions and fatal error handling. Keep vfs.Stat for the assertion.

Source: Learnings

🤖 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/auth/login_scope_cache.go`:
- Around line 101-103: Update the expiration branch in the scope-cache lookup to
remove both the latest app record and the corresponding device-scoped record
before returning os.ErrNotExist. Extend the expiration test to verify that both
cache files are deleted.
- Around line 135-146: Update removePendingLogin to preserve vfs.ReadFile errors
when the pending-login file exists but cannot be read, while treating
os.ErrNotExist as benign. Propagate the read error through firstErr when no
earlier cleanup error has been recorded, and keep the existing record-matching
removal behavior unchanged.

In `@cmd/auth/login.go`:
- Around line 90-92: Add command-level flag-parsing coverage for the `--resume`
option in the login command tests, asserting that parsing `--resume` sets
`LoginOptions.Resume` to true. Keep the existing parsing coverage unchanged and
place the assertion alongside the other login flag tests.
- Around line 294-296: Update the savePendingLogin error path in the login
command so --no-wait returns a typed storage error that preserves the underlying
cause instead of logging a warning and continuing; keep existing behavior for
other modes. Add an error-path test verifying the returned error type and
wrapped cause metadata.
- Around line 139-149: Update the missing-pending-state branch in the
opts.Resume flow to return
errs.NewValidationError(errs.SubtypeFailedPrecondition, ...) instead of an
authentication/unknown error, preserving the existing recovery hint. Add
metadata assertions using errs.ProblemOf to verify the returned problem has the
expected validation category and failed-precondition subtype.

In `@cmd/config/init_guard_test.go`:
- Around line 23-44: Update TestGuardAgentWorkspace_ManagedProfileRefuses to
inspect the typed problem metadata with errs.ProblemOf, asserting CategoryConfig
and SubtypeNotConfigured (and preserving the existing cause assertion pattern if
applicable). Retain the current error-type and message/hint checks, but make the
test validate typed metadata rather than relying only on strings.

---

Nitpick comments:
In `@cmd/auth/login_scope_cache_test.go`:
- Around line 98-102: Update the fixture setup around loginScopeCacheDir and
pendingLoginPath to use os.MkdirAll and os.WriteFile instead of vfs.MkdirAll and
vfs.WriteFile, preserving the existing permissions and fatal error handling.
Keep vfs.Stat for the assertion.
🪄 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: d05da9b8-155d-4e8a-8b66-9a6842b1f1ab

📥 Commits

Reviewing files that changed from the base of the PR and between e79d49e and 75793a8.

📒 Files selected for processing (10)
  • cmd/auth/auth_test.go
  • cmd/auth/login.go
  • cmd/auth/login_messages.go
  • cmd/auth/login_messages_test.go
  • cmd/auth/login_scope_cache.go
  • cmd/auth/login_scope_cache_test.go
  • cmd/auth/login_test.go
  • cmd/config/init.go
  • cmd/config/init_guard_test.go
  • skills/lark-shared/SKILL.md

Comment on lines +101 to +103
if record.DeviceCode == "" || record.AppID != appID || record.ExpiresAt <= time.Now().Unix() {
_ = vfs.Remove(path)
return nil, os.ErrNotExist

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Remove the expired flow’s per-device scope record too.

Expiration deletes only latest-<app>.json, leaving the device-scoped cache indefinitely. Clean both records and extend the expiration test to assert both files disappear.

Proposed fix
-	if record.DeviceCode == "" || record.AppID != appID || record.ExpiresAt <= time.Now().Unix() {
+	if record.DeviceCode == "" || record.AppID != appID {
 		_ = vfs.Remove(path)
 		return nil, os.ErrNotExist
 	}
+	if record.ExpiresAt <= time.Now().Unix() {
+		_ = removeLoginRequestedScope(record.DeviceCode)
+		_ = vfs.Remove(path)
+		return nil, os.ErrNotExist
+	}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if record.DeviceCode == "" || record.AppID != appID || record.ExpiresAt <= time.Now().Unix() {
_ = vfs.Remove(path)
return nil, os.ErrNotExist
if record.DeviceCode == "" || record.AppID != appID {
_ = vfs.Remove(path)
return nil, os.ErrNotExist
}
if record.ExpiresAt <= time.Now().Unix() {
_ = removeLoginRequestedScope(record.DeviceCode)
_ = vfs.Remove(path)
return nil, os.ErrNotExist
}
🤖 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/auth/login_scope_cache.go` around lines 101 - 103, Update the expiration
branch in the scope-cache lookup to remove both the latest app record and the
corresponding device-scoped record before returning os.ErrNotExist. Extend the
expiration test to verify that both cache files are deleted.

Comment on lines +135 to +146
func removePendingLogin(deviceCode, appID string) error {
firstErr := removeLoginRequestedScope(deviceCode)
path := pendingLoginPath(appID)
if data, err := vfs.ReadFile(path); err == nil {
var record pendingLoginRecord
if json.Unmarshal(data, &record) == nil && record.DeviceCode == deviceCode {
if err := vfs.Remove(path); err != nil && !errors.Is(err, os.ErrNotExist) && firstErr == nil {
firstErr = err
}
}
}
return firstErr

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Do not silently ignore pending-file read failures.

If vfs.ReadFile fails for reasons other than absence, cleanup returns success and leaves stale resumable state behind. Preserve that error so the command can warn the caller.

Proposed fix
 	path := pendingLoginPath(appID)
-	if data, err := vfs.ReadFile(path); err == nil {
+	data, err := vfs.ReadFile(path)
+	if err != nil {
+		if !errors.Is(err, os.ErrNotExist) && firstErr == nil {
+			firstErr = err
+		}
+		return firstErr
+	}
+	{
 		var record pendingLoginRecord
 		if json.Unmarshal(data, &record) == nil && record.DeviceCode == deviceCode {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
func removePendingLogin(deviceCode, appID string) error {
firstErr := removeLoginRequestedScope(deviceCode)
path := pendingLoginPath(appID)
if data, err := vfs.ReadFile(path); err == nil {
var record pendingLoginRecord
if json.Unmarshal(data, &record) == nil && record.DeviceCode == deviceCode {
if err := vfs.Remove(path); err != nil && !errors.Is(err, os.ErrNotExist) && firstErr == nil {
firstErr = err
}
}
}
return firstErr
func removePendingLogin(deviceCode, appID string) error {
firstErr := removeLoginRequestedScope(deviceCode)
path := pendingLoginPath(appID)
data, err := vfs.ReadFile(path)
if err != nil {
if !errors.Is(err, os.ErrNotExist) && firstErr == nil {
firstErr = err
}
return firstErr
}
{
var record pendingLoginRecord
if json.Unmarshal(data, &record) == nil && record.DeviceCode == deviceCode {
if err := vfs.Remove(path); err != nil && !errors.Is(err, os.ErrNotExist) && firstErr == nil {
firstErr = err
}
}
}
return firstErr
🤖 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/auth/login_scope_cache.go` around lines 135 - 146, Update
removePendingLogin to preserve vfs.ReadFile errors when the pending-login file
exists but cannot be read, while treating os.ErrNotExist as benign. Propagate
the read error through firstErr when no earlier cleanup error has been recorded,
and keep the existing record-matching removal behavior unchanged.

Comment thread cmd/auth/login.go
Comment on lines +90 to +92
cmd.Flags().BoolVar(&opts.NoWait, "no-wait", false, "initiate device authorization and return immediately; use --resume to complete later")
cmd.Flags().StringVar(&opts.DeviceCode, "device-code", "", "poll and complete authorization with a device code from a previous --no-wait call")
cmd.Flags().BoolVar(&opts.Resume, "resume", false, "resume the latest pending authorization created by --no-wait")

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Add direct parsing coverage for --resume.

The new Cobra flag is not exercised by the supplied flag-parsing test. Add a command-level assertion that --resume sets LoginOptions.Resume.

As per coding guidelines, every behavior change needs a test alongside the change.

🤖 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/auth/login.go` around lines 90 - 92, Add command-level flag-parsing
coverage for the `--resume` option in the login command tests, asserting that
parsing `--resume` sets `LoginOptions.Resume` to true. Keep the existing parsing
coverage unchanged and place the assertion alongside the other login flag tests.

Source: Coding guidelines

Comment thread cmd/auth/login.go
Comment on lines +139 to +149
if opts.Resume {
if opts.DeviceCode != "" || opts.NoWait || opts.Scope != "" || opts.Recommend || len(opts.Domains) > 0 || len(opts.Exclude) > 0 {
return errs.NewValidationError(errs.SubtypeInvalidArgument, "--resume cannot be combined with authorization request options or --device-code").WithParam("--resume")
}
pending, err := loadPendingLogin(config.AppID)
if err != nil {
if errors.Is(err, os.ErrNotExist) {
return errs.NewAuthenticationError(errs.SubtypeUnknown, "no unexpired pending authorization to resume").
WithHint("start a fresh split flow with `lark-cli auth login --scope <scope> --no-wait --json`, send its verification URL to the user, then run `lark-cli auth login --resume` only after the user confirms authorization")
}
return errs.NewInternalError(errs.SubtypeStorage, "failed to load pending authorization: %v", err).WithCause(err)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Classify missing pending state as a failed precondition.

--resume is valid input, but no resumable state exists. Returning AuthenticationError/Unknown breaks the typed command contract; use ValidationError/FailedPrecondition and retain the recovery hint. Add metadata assertions using errs.ProblemOf.

As per coding guidelines, valid requests made in the wrong system state must use errs.NewValidationError(errs.SubtypeFailedPrecondition, ...).WithHint(...).

Proposed fix
-				return errs.NewAuthenticationError(errs.SubtypeUnknown, "no unexpired pending authorization to resume").
+				return errs.NewValidationError(errs.SubtypeFailedPrecondition, "no unexpired pending authorization to resume").
 					WithHint("start a fresh split flow with `lark-cli auth login --scope <scope> --no-wait --json`, send its verification URL to the user, then run `lark-cli auth login --resume` only after the user confirms authorization")
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if opts.Resume {
if opts.DeviceCode != "" || opts.NoWait || opts.Scope != "" || opts.Recommend || len(opts.Domains) > 0 || len(opts.Exclude) > 0 {
return errs.NewValidationError(errs.SubtypeInvalidArgument, "--resume cannot be combined with authorization request options or --device-code").WithParam("--resume")
}
pending, err := loadPendingLogin(config.AppID)
if err != nil {
if errors.Is(err, os.ErrNotExist) {
return errs.NewAuthenticationError(errs.SubtypeUnknown, "no unexpired pending authorization to resume").
WithHint("start a fresh split flow with `lark-cli auth login --scope <scope> --no-wait --json`, send its verification URL to the user, then run `lark-cli auth login --resume` only after the user confirms authorization")
}
return errs.NewInternalError(errs.SubtypeStorage, "failed to load pending authorization: %v", err).WithCause(err)
if opts.Resume {
if opts.DeviceCode != "" || opts.NoWait || opts.Scope != "" || opts.Recommend || len(opts.Domains) > 0 || len(opts.Exclude) > 0 {
return errs.NewValidationError(errs.SubtypeInvalidArgument, "--resume cannot be combined with authorization request options or --device-code").WithParam("--resume")
}
pending, err := loadPendingLogin(config.AppID)
if err != nil {
if errors.Is(err, os.ErrNotExist) {
return errs.NewValidationError(errs.SubtypeFailedPrecondition, "no unexpired pending authorization to resume").
WithHint("start a fresh split flow with `lark-cli auth login --scope <scope> --no-wait --json`, send its verification URL to the user, then run `lark-cli auth login --resume` only after the user confirms authorization")
}
return errs.NewInternalError(errs.SubtypeStorage, "failed to load pending authorization: %v", err).WithCause(err)
🤖 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/auth/login.go` around lines 139 - 149, Update the missing-pending-state
branch in the opts.Resume flow to return
errs.NewValidationError(errs.SubtypeFailedPrecondition, ...) instead of an
authentication/unknown error, preserving the existing recovery hint. Add
metadata assertions using errs.ProblemOf to verify the returned problem has the
expected validation category and failed-precondition subtype.

Source: Coding guidelines

Comment thread cmd/auth/login.go
Comment on lines +294 to 296
if err := savePendingLogin(authResp.DeviceCode, config.AppID, finalScope, authResp.ExpiresIn); err != nil {
fmt.Fprintf(f.IOStreams.ErrOut, "[lark-cli] [WARN] auth login: failed to cache requested scopes: %v\n", err)
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Fail --no-wait when resumable state cannot be persisted.

The command currently returns success and instructs the agent to use --resume, even though that follow-up is guaranteed to fail. Return a typed storage error with the underlying cause, and add an error-path metadata/cause test.

Proposed fix
 	if opts.NoWait {
 		if err := savePendingLogin(authResp.DeviceCode, config.AppID, finalScope, authResp.ExpiresIn); err != nil {
-			fmt.Fprintf(f.IOStreams.ErrOut, "[lark-cli] [WARN] auth login: failed to cache requested scopes: %v\n", err)
+			return errs.NewInternalError(errs.SubtypeStorage,
+				"failed to save pending authorization: %v", err).WithCause(err)
 		}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if err := savePendingLogin(authResp.DeviceCode, config.AppID, finalScope, authResp.ExpiresIn); err != nil {
fmt.Fprintf(f.IOStreams.ErrOut, "[lark-cli] [WARN] auth login: failed to cache requested scopes: %v\n", err)
}
if err := savePendingLogin(authResp.DeviceCode, config.AppID, finalScope, authResp.ExpiresIn); err != nil {
return errs.NewInternalError(errs.SubtypeStorage,
"failed to save pending authorization: %v", err).WithCause(err)
}
🤖 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/auth/login.go` around lines 294 - 296, Update the savePendingLogin error
path in the login command so --no-wait returns a typed storage error that
preserves the underlying cause instead of logging a warning and continuing; keep
existing behavior for other modes. Add an error-path test verifying the returned
error type and wrapped cause metadata.

Source: Coding guidelines

Comment on lines +23 to +44
func TestGuardAgentWorkspace_ManagedProfileRefuses(t *testing.T) {
clearAgentEnv(t)
t.Setenv("LARKSUITE_CLI_MANAGED_CONFIG", "1")

err := guardAgentWorkspace(&ConfigInitOptions{})
if err == nil {
t.Fatal("expected refusal for a host-managed profile, got nil")
}
var cfgErr *errs.ConfigError
if !errors.As(err, &cfgErr) {
t.Fatalf("error type = %T, want *errs.ConfigError", err)
}
if !strings.Contains(cfgErr.Message, "managed by the Agent host") {
t.Errorf("message must identify host-managed config; got %q", cfgErr.Message)
}
if strings.Contains(cfgErr.Hint, "config bind") {
t.Errorf("managed profiles must not be redirected to config bind; got %q", cfgErr.Hint)
}
if !strings.Contains(cfgErr.Hint, "failed to provision") {
t.Errorf("hint must identify the host provisioning failure; got %q", cfgErr.Hint)
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Assert typed error subtype, not just message substrings.

Per the coding guidelines, error-path tests must assert typed metadata via errs.ProblemOf (category / subtype / param) and cause preservation, not message substrings alone. TestGuardAgentWorkspace_ManagedProfileRefuses checks the error type via errors.As and verifies message/hint substrings, but does not assert Subtype == errs.SubtypeNotConfigured (or Category == CategoryConfig). Add a subtype assertion to satisfy the guideline.

🛡️ Proposed fix: add subtype assertion
 	if !errors.As(err, &cfgErr) {
 		t.Fatalf("error type = %T, want *errs.ConfigError", err)
 	}
+	if cfgErr.Subtype != errs.SubtypeNotConfigured {
+		t.Errorf("subtype = %q, want %q", cfgErr.Subtype, errs.SubtypeNotConfigured)
+	}
 	if !strings.Contains(cfgErr.Message, "managed by the Agent host") {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
func TestGuardAgentWorkspace_ManagedProfileRefuses(t *testing.T) {
clearAgentEnv(t)
t.Setenv("LARKSUITE_CLI_MANAGED_CONFIG", "1")
err := guardAgentWorkspace(&ConfigInitOptions{})
if err == nil {
t.Fatal("expected refusal for a host-managed profile, got nil")
}
var cfgErr *errs.ConfigError
if !errors.As(err, &cfgErr) {
t.Fatalf("error type = %T, want *errs.ConfigError", err)
}
if !strings.Contains(cfgErr.Message, "managed by the Agent host") {
t.Errorf("message must identify host-managed config; got %q", cfgErr.Message)
}
if strings.Contains(cfgErr.Hint, "config bind") {
t.Errorf("managed profiles must not be redirected to config bind; got %q", cfgErr.Hint)
}
if !strings.Contains(cfgErr.Hint, "failed to provision") {
t.Errorf("hint must identify the host provisioning failure; got %q", cfgErr.Hint)
}
}
func TestGuardAgentWorkspace_ManagedProfileRefuses(t *testing.T) {
clearAgentEnv(t)
t.Setenv("LARKSUITE_CLI_MANAGED_CONFIG", "1")
err := guardAgentWorkspace(&ConfigInitOptions{})
if err == nil {
t.Fatal("expected refusal for a host-managed profile, got nil")
}
var cfgErr *errs.ConfigError
if !errors.As(err, &cfgErr) {
t.Fatalf("error type = %T, want *errs.ConfigError", err)
}
if cfgErr.Subtype != errs.SubtypeNotConfigured {
t.Errorf("subtype = %q, want %q", cfgErr.Subtype, errs.SubtypeNotConfigured)
}
if !strings.Contains(cfgErr.Message, "managed by the Agent host") {
t.Errorf("message must identify host-managed config; got %q", cfgErr.Message)
}
if strings.Contains(cfgErr.Hint, "config bind") {
t.Errorf("managed profiles must not be redirected to config bind; got %q", cfgErr.Hint)
}
if !strings.Contains(cfgErr.Hint, "failed to provision") {
t.Errorf("hint must identify the host provisioning failure; got %q", cfgErr.Hint)
}
}
🤖 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/config/init_guard_test.go` around lines 23 - 44, Update
TestGuardAgentWorkspace_ManagedProfileRefuses to inspect the typed problem
metadata with errs.ProblemOf, asserting CategoryConfig and SubtypeNotConfigured
(and preserving the existing cause assertion pattern if applicable). Retain the
current error-type and message/hint checks, but make the test validate typed
metadata rather than relying only on strings.

Source: Coding guidelines

@CLAassistant

Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.


yilin.wang seems not to be a GitHub user. You need a GitHub account to be able to sign the CLA. If you have already a GitHub account, please add the email address used for this commit to your account.
You have signed the CLA already but the status is still pending? Let us recheck it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants