Skip to content

feat(feed): add +sensitive shortcut for time-sensitive status#717

Closed
jinzemo wants to merge 10 commits intolarksuite:mainfrom
jinzemo:feat/im-feed-card-time-sensitive
Closed

feat(feed): add +sensitive shortcut for time-sensitive status#717
jinzemo wants to merge 10 commits intolarksuite:mainfrom
jinzemo:feat/im-feed-card-time-sensitive

Conversation

@jinzemo
Copy link
Copy Markdown

@jinzemo jinzemo commented Apr 29, 2026

Summary

Add a new feed domain with a single shortcut +sensitive that calls the Lark PATCH /im/v2/feed_cards/:feed_card_id API to set or unset the time-sensitive (即时提醒/置顶) status of a feed card for specified users in a group chat.

Changes

  • New shortcuts/feed/ package with +sensitive command
  • Validation: --enable/--disable mutual exclusion, oc_ prefix check for feed-card-id
  • Partial failure handling: warnings to stderr, exit 1 with structured error
  • Skill docs: skills/lark-feed/SKILL.md and references/lark-feed-sensitive.md
  • E2E tests: 4 scenarios (enable/disable/dry-run/validation)

Test Plan

  • Unit tests: 8/8 pass (validation + execute with httpmock)
  • E2E tests: 4/4 pass (sandbox)
  • Skill eval: 7/7 pass (sandbox)
  • Manual dry-run verification

Summary by CodeRabbit

  • New Features

    • Added feed +sensitive command to manage Feed Card time-sensitive state for group chats. Enable or disable per-user time-sensitive behavior using --enable/--disable flags. Supports multiple user IDs and --dry-run mode for API request preview.
  • Documentation

    • Added lark-feed skill documentation and feed +sensitive command reference guide, including usage examples, prerequisites, and exit code information.

@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.


maojinze.7 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.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 29, 2026

📝 Walkthrough

Walkthrough

This PR introduces a new feed +sensitive shortcut command that manages Feed Card time-sensitive state by issuing PATCH requests to the Lark API. Includes validation, dry-run support, comprehensive unit and e2e tests, full documentation, type alias support, and registry integration.

Changes

Cohort / File(s) Summary
Type System
shortcuts/common/types.go
Extended Flag.Type documentation to include "string_slice" as a supported type name.
Feed Sensitive Shortcut
shortcuts/feed/feed_sensitive.go, shortcuts/feed/shortcuts.go
Implemented new +sensitive command with PATCH request to /open-apis/im/v2/feed_cards/{feedCardId}, supporting --enable/--disable flags, user ID validation, dry-run mode, and structured error reporting from API responses.
Shortcut Tests
shortcuts/feed/feed_sensitive_test.go, shortcuts/feed/shortcuts_test.go
Added unit tests validating parameter validation (mutual exclusivity, required flags, valid chat IDs), execution with mocked HTTP responses, and shortcut registry behavior.
Registry Integration
shortcuts/register.go
Updated shortcut registry to include feed-related shortcuts via feed.Shortcuts() initialization.
Reference Documentation
skills/lark-feed/SKILL.md, skills/lark-feed/references/lark-feed-sensitive.md
Added skill definition and detailed reference documentation covering prerequisites, scope requirements, usage patterns, response fields, exit codes, and validation constraints.
End-to-End Tests
tests_e2e/feed/2026_04_29_feed_sensitive_test.go
Added e2e test suite validating enable/disable workflows, dry-run behavior, validation error handling, and JSON response structure with helper utilities.

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant CLI as CLI Handler
    participant Validator
    participant APIClient
    participant APIServer as Lark API
    participant Formatter

    User->>CLI: feed +sensitive --enable/disable --feed-card-id <id> --user-ids <ids>
    
    CLI->>Validator: Validate parameters
    Validator->>Validator: Check --enable/--disable mutual exclusivity
    Validator->>Validator: Validate chat ID format (oc_ prefix)
    Validator->>Validator: Check non-empty user IDs
    
    alt Validation fails
        Validator-->>CLI: Return validation error
        CLI-->>User: Exit with error message
    else Validation passes
        Validator-->>CLI: OK
        
        alt Dry-run mode
            CLI->>APIClient: Build PATCH request (no execute)
            APIClient-->>CLI: Request preview
        else Normal execution
            CLI->>APIClient: Build PATCH request
            APIClient->>APIServer: PATCH /open-apis/im/v2/feed_cards/{feedCardId}
            APIServer-->>APIClient: Response with failed_user_reasons
        end
        
        APIClient-->>Formatter: Parsed response
        Formatter->>Formatter: Extract failed_user_reasons by user
        
        alt All users succeeded
            Formatter-->>CLI: Success summary
            CLI-->>User: Exit code 0, output success
        else Partial failures
            Formatter-->>CLI: Success summary + warnings
            CLI-->>User: Exit API error, stderr warnings
        else All users failed
            Formatter-->>CLI: All failed error
            CLI-->>User: Exit API error, stderr warnings
        end
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested labels

domain/ccm, size/L

Suggested reviewers

  • fangshuyu-768
  • liujinkun2025

Poem

🐰 A feed card now whispers its secrets to time,
With +sensitive hops, we adjust its chime,
Enable, disable, and dry runs so fine,
Tests march in formation—a well-threaded line,
The shortcuts grow mighty, one hop at a time! 🌟

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 38.89% 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 clearly and concisely describes the main change: adding a new +sensitive shortcut for managing feed card time-sensitive status.
Description check ✅ Passed The pull request description follows the template structure with Summary, Changes, and Test Plan sections, all appropriately filled with relevant details.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ 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
Review rate limit: 7/8 reviews remaining, refill in 7 minutes and 30 seconds.

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

@github-actions github-actions Bot added the size/L Large or sensitive change across domains or core paths label Apr 29, 2026
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Nitpick comments (1)
tests_e2e/feed/2026_04_29_feed_sensitive_test.go (1)

221-262: Drop the unused parentT parameter.

createChatForFeed only keeps parentT to silence the unused variable, which makes the helper noisier than necessary. Removing it would simplify both the helper and its callers.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests_e2e/feed/2026_04_29_feed_sensitive_test.go` around lines 221 - 262, The
helper createChatForFeed currently accepts an unused parentT parameter which is
only referenced as `_ = parentT`; remove the unused parameter by changing the
signature from func createChatForFeed(t *testing.T, parentT *testing.T, ctx
context.Context, name string) string to func createChatForFeed(t *testing.T, ctx
context.Context, name string) string, delete the `_ = parentT` line, and update
all call sites to pass only (t, ctx, name) so callers compile cleanly and the
helper is simplified.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@skills/lark-feed/references/lark-feed-sensitive.md`:
- Around line 77-84: Update the "退出码" table to distinguish Validate failures
during dry-run flows from ordinary parameter validation errors: keep the
existing row for "参数校验错误" with exit code 1 for normal runs, and add a new row
(or annotate) stating that validation failures that occur inside the Validate
step during dry-run invocations exit with code 2 and emit the structured JSON
error envelope; reference the Validate step and the dry-run handler (e.g.,
"Validate" and "dry-run") so readers can locate where this behavior originates.
- Around line 67-75: Add the Markdown language tag "text" to both fenced code
blocks in the examples so they become ```text ... ```, specifically update the
block containing "Time-sensitive updated for 2 user(s) (feed_card_id: oc_xxx)"
and the warning block that starts with "warning: 1 user(s) failed:" so they pass
markdownlint MD040; keep the content unchanged, only add the language specifier
to the opening fence of each block.

In `@tests_e2e/feed/2026_04_29_feed_sensitive_test.go`:
- Around line 148-219: Update TestFeedSensitive_ValidationErrors to assert the
specific validation failure semantics instead of a broad non-zero check: replace
the assert.NotEqual(t, 0, result.ExitCode) checks with assert.Equal(t, 2,
result.ExitCode) for each subtest (use the same result.ExitCode from
clie2e.RunCmd), and add assertions on result.Stdout to confirm the structured
JSON error envelope (unmarshal or use JSON assertions to verify an error object
with a validation/validate stage indicator and a message referencing the
offending flag or "oc_" prefix). Ensure you reference the existing symbols
TestFeedSensitive_ValidationErrors, clie2e.RunCmd, result.ExitCode, and
result.Stdout when making these changes.
- Around line 118-145: TestFeedSensitive_DryRunFlagBehavior runs clie2e.RunCmd
without setting test-local env vars so it may pick up ambient credentials;
before calling clie2e.RunCmd in that subtest, set the four env vars using
t.Setenv: LARKSUITE_CLI_CONFIG_DIR (use t.TempDir()), LARKSUITE_CLI_APP_ID
("app"), LARKSUITE_CLI_APP_SECRET ("secret"), and LARKSUITE_CLI_BRAND ("feishu")
so the dry-run is isolated from real credentials and mirrors other dry-run tests
(refer to TestFeedSensitive_DryRunFlagBehavior and the clie2e.RunCmd invocation
to locate where to add the t.Setenv calls).

---

Nitpick comments:
In `@tests_e2e/feed/2026_04_29_feed_sensitive_test.go`:
- Around line 221-262: The helper createChatForFeed currently accepts an unused
parentT parameter which is only referenced as `_ = parentT`; remove the unused
parameter by changing the signature from func createChatForFeed(t *testing.T,
parentT *testing.T, ctx context.Context, name string) string to func
createChatForFeed(t *testing.T, ctx context.Context, name string) string, delete
the `_ = parentT` line, and update all call sites to pass only (t, ctx, name) so
callers compile cleanly and the helper is simplified.
🪄 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: 09fd7ed4-3a4f-484b-8360-d0982ca7c8ab

📥 Commits

Reviewing files that changed from the base of the PR and between 4181925 and 715b2da.

📒 Files selected for processing (9)
  • shortcuts/common/types.go
  • shortcuts/feed/feed_sensitive.go
  • shortcuts/feed/feed_sensitive_test.go
  • shortcuts/feed/shortcuts.go
  • shortcuts/feed/shortcuts_test.go
  • shortcuts/register.go
  • skills/lark-feed/SKILL.md
  • skills/lark-feed/references/lark-feed-sensitive.md
  • tests_e2e/feed/2026_04_29_feed_sensitive_test.go

Comment on lines +67 to +75
```
Time-sensitive updated for 2 user(s) (feed_card_id: oc_xxx)
```

部分失败时,stdout 显示成功数,stderr 显示警告:
```
warning: 1 user(s) failed:
ou_yyy: The user is not in the chat
```
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Add a language tag to the output examples.

The plain fences trip markdownlint MD040.

Suggested fix
-```
+```text
 Time-sensitive updated for 2 user(s) (feed_card_id: oc_xxx)
-```
+```text

Apply the same text tag to the warning block below.

🧰 Tools
🪛 markdownlint-cli2 (0.22.1)

[warning] 67-67: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


[warning] 72-72: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@skills/lark-feed/references/lark-feed-sensitive.md` around lines 67 - 75, Add
the Markdown language tag "text" to both fenced code blocks in the examples so
they become ```text ... ```, specifically update the block containing
"Time-sensitive updated for 2 user(s) (feed_card_id: oc_xxx)" and the warning
block that starts with "warning: 1 user(s) failed:" so they pass markdownlint
MD040; keep the content unchanged, only add the language specifier to the
opening fence of each block.

Comment on lines +77 to +84
## 退出码

| 条件 | 退出码 |
|------|--------|
| 全部成功 | 0 |
| 部分或全部用户失败 | 1 |
| 参数校验错误 | 1 |
| API 错误 | 1 |
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Clarify the validation exit code for dry-run flows.

The table currently says parameter validation errors exit 1, but Validate failures in dry-run invocations happen before the dry-run handler and exit 2. Please split that row or annotate the dry-run exception.

Suggested fix
-| 参数校验错误 | 1 |
+| 参数校验错误(非 dry-run) | 1 |
+| 参数校验错误(dry-run) | 2 |

Based on learnings, validation failures inside Validate for dry-run flows exit with code 2 and emit the structured JSON error envelope.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@skills/lark-feed/references/lark-feed-sensitive.md` around lines 77 - 84,
Update the "退出码" table to distinguish Validate failures during dry-run flows
from ordinary parameter validation errors: keep the existing row for "参数校验错误"
with exit code 1 for normal runs, and add a new row (or annotate) stating that
validation failures that occur inside the Validate step during dry-run
invocations exit with code 2 and emit the structured JSON error envelope;
reference the Validate step and the dry-run handler (e.g., "Validate" and
"dry-run") so readers can locate where this behavior originates.

Comment on lines +118 to +145
// TestFeedSensitive_DryRunFlagBehavior proves that --dry-run produces a PATCH
// request preview on stdout and exits 0 without making a real API call.
func TestFeedSensitive_DryRunFlagBehavior(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
t.Cleanup(cancel)

t.Run("dry-run prints PATCH preview", func(t *testing.T) {
result, err := clie2e.RunCmd(ctx, clie2e.Request{
Args: []string{
"feed", "+sensitive",
"--feed-card-id", "oc_dryruntestid",
"--enable",
"--user-ids", sandboxUserOpenID,
"--dry-run",
},
DefaultAs: "bot",
})
require.NoError(t, err)
result.AssertExitCode(t, 0)

combined := result.Stdout + result.Stderr
assert.Contains(t, combined, "PATCH",
"dry-run output must contain HTTP method PATCH, combined:\n%s", combined)
assert.Contains(t, combined, "/im/v2/feed_cards/",
"dry-run output must contain the feed_cards API path, combined:\n%s", combined)
assert.Contains(t, combined, "oc_dryruntestid",
"dry-run output must contain the feed_card_id path segment, combined:\n%s", combined)
})
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
rg -n "LARKSUITE_CLI_(APP_ID|APP_SECRET|CONFIG_DIR)|BRAND" tests/cli_e2e shortcuts

Repository: larksuite/cli

Length of output: 4053


🏁 Script executed:

#!/bin/bash
# Find the feed_sensitive test file
find . -name "*feed*sensitive*test.go" -type f

Repository: larksuite/cli

Length of output: 148


🏁 Script executed:

#!/bin/bash
# Also search in tests directory structure
find tests* -name "*feed*" -type d 2>/dev/null | head -20

Repository: larksuite/cli

Length of output: 72


🏁 Script executed:

#!/bin/bash
# Broader search for feed tests
find . -path "*/feed/*test.go" -type f

Repository: larksuite/cli

Length of output: 183


🏁 Script executed:

#!/bin/bash
# Read the feed test file to check for env vars
cat -n tests_e2e/feed/2026_04_29_feed_sensitive_test.go

Repository: larksuite/cli

Length of output: 11120


Add env vars to isolate the dry-run test from ambient credentials.

The test at lines 124-145 must set LARKSUITE_CLI_APP_ID, APP_SECRET, BRAND, and LARKSUITE_CLI_CONFIG_DIR before calling clie2e.RunCmd(). Other dry-run tests in the suite (drive, calendar, mail, okr) already do this; this test should follow the same pattern:

t.Setenv("LARKSUITE_CLI_CONFIG_DIR", t.TempDir())
t.Setenv("LARKSUITE_CLI_APP_ID", "app")
t.Setenv("LARKSUITE_CLI_APP_SECRET", "secret")
t.Setenv("LARKSUITE_CLI_BRAND", "feishu")

Without these, the test will inherit the developer's real credentials or fail only in CI.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests_e2e/feed/2026_04_29_feed_sensitive_test.go` around lines 118 - 145,
TestFeedSensitive_DryRunFlagBehavior runs clie2e.RunCmd without setting
test-local env vars so it may pick up ambient credentials; before calling
clie2e.RunCmd in that subtest, set the four env vars using t.Setenv:
LARKSUITE_CLI_CONFIG_DIR (use t.TempDir()), LARKSUITE_CLI_APP_ID ("app"),
LARKSUITE_CLI_APP_SECRET ("secret"), and LARKSUITE_CLI_BRAND ("feishu") so the
dry-run is isolated from real credentials and mirrors other dry-run tests (refer
to TestFeedSensitive_DryRunFlagBehavior and the clie2e.RunCmd invocation to
locate where to add the t.Setenv calls).

Comment on lines +148 to +219
// TestFeedSensitive_ValidationErrors proves the CLI validation layer:
// - missing --enable/--disable → exit non-zero with a descriptive error
// - both --enable and --disable together → exit non-zero (mutual exclusion)
// - invalid feed-card-id (no oc_ prefix) → exit non-zero with validation error
//
// The spec says exit 1 for validation errors; the CLI framework may return exit 2
// for flag-parse failures. Both are accepted here as "not 0".
func TestFeedSensitive_ValidationErrors(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
t.Cleanup(cancel)

t.Run("missing enable or disable flag", func(t *testing.T) {
result, err := clie2e.RunCmd(ctx, clie2e.Request{
Args: []string{
"feed", "+sensitive",
"--feed-card-id", "oc_testid",
"--user-ids", sandboxUserOpenID,
// intentionally omitting --enable and --disable
},
DefaultAs: "bot",
})
require.NoError(t, err)
assert.NotEqual(t, 0, result.ExitCode,
"must fail when neither --enable nor --disable is provided, stdout:\n%s\nstderr:\n%s",
result.Stdout, result.Stderr)

combined := result.Stdout + result.Stderr
assert.Contains(t, combined, "--enable",
"error message must mention --enable flag, combined:\n%s", combined)
})

t.Run("enable and disable are mutually exclusive", func(t *testing.T) {
result, err := clie2e.RunCmd(ctx, clie2e.Request{
Args: []string{
"feed", "+sensitive",
"--feed-card-id", "oc_testid",
"--enable",
"--disable",
"--user-ids", sandboxUserOpenID,
},
DefaultAs: "bot",
})
require.NoError(t, err)
assert.NotEqual(t, 0, result.ExitCode,
"must fail when both --enable and --disable are provided, stdout:\n%s\nstderr:\n%s",
result.Stdout, result.Stderr)

combined := result.Stdout + result.Stderr
assert.Contains(t, combined, "--disable",
"error message must mention --disable flag, combined:\n%s", combined)
})

t.Run("invalid feed-card-id without oc_ prefix", func(t *testing.T) {
result, err := clie2e.RunCmd(ctx, clie2e.Request{
Args: []string{
"feed", "+sensitive",
"--feed-card-id", "invalid_id_no_oc_prefix",
"--enable",
"--user-ids", sandboxUserOpenID,
},
DefaultAs: "bot",
})
require.NoError(t, err)
assert.NotEqual(t, 0, result.ExitCode,
"must fail for feed-card-id that does not start with oc_, stdout:\n%s\nstderr:\n%s",
result.Stdout, result.Stderr)

combined := result.Stdout + result.Stderr
assert.Contains(t, combined, "oc_",
"error message must mention the oc_ prefix requirement, combined:\n%s", combined)
})
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Tighten the negative-path assertions.

assert.NotEqual(..., 0) is broad enough to pass on unrelated startup failures. These validation cases should assert the actual validation exit (2) and structured JSON error envelope so regressions in +sensitive validation don't slip through.

Based on learnings: Validate-stage rejects exit code 2 and a structured JSON error envelope to stdout.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests_e2e/feed/2026_04_29_feed_sensitive_test.go` around lines 148 - 219,
Update TestFeedSensitive_ValidationErrors to assert the specific validation
failure semantics instead of a broad non-zero check: replace the
assert.NotEqual(t, 0, result.ExitCode) checks with assert.Equal(t, 2,
result.ExitCode) for each subtest (use the same result.ExitCode from
clie2e.RunCmd), and add assertions on result.Stdout to confirm the structured
JSON error envelope (unmarshal or use JSON assertions to verify an error object
with a validation/validate stage indicator and a message referencing the
offending flag or "oc_" prefix). Ensure you reference the existing symbols
TestFeedSensitive_ValidationErrors, clie2e.RunCmd, result.ExitCode, and
result.Stdout when making these changes.

@jinzemo jinzemo closed this Apr 29, 2026
@jinzemo jinzemo deleted the feat/im-feed-card-time-sensitive branch April 29, 2026 11:04
@jinzemo jinzemo restored the feat/im-feed-card-time-sensitive branch April 29, 2026 11:05
@jinzemo jinzemo deleted the feat/im-feed-card-time-sensitive branch April 29, 2026 12:02
@jinzemo jinzemo restored the feat/im-feed-card-time-sensitive branch April 29, 2026 12:03
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