Skip to content

docs(skills): slim SKILL.md context and token usage across domains#1452

Open
liangshuo-1 wants to merge 7 commits into
mainfrom
eval/skills-combined
Open

docs(skills): slim SKILL.md context and token usage across domains#1452
liangshuo-1 wants to merge 7 commits into
mainfrom
eval/skills-combined

Conversation

@liangshuo-1

@liangshuo-1 liangshuo-1 commented Jun 13, 2026

Copy link
Copy Markdown
Collaborator

精简多个域 skill 的常驻上下文,降低 token 占用并提升路由 / 可执行性:

  • 各域 SKILL.md 与 references 按「可执行优先 + 按需加载」重排,冗余资源表 / scope 表收敛到 --help / schema
  • base record / field shortcuts 增加容错 flag alias 与误用提示
  • workflow 引导按 step 拆为独立 references

liangshuo-1 and others added 7 commits June 13, 2026 17:58
…erences

Rewrite the always-loaded SKILL.md from a 168-line monolith into a slim
core: a positioning line plus 7 mental-model "通用准则" ordered by agent
attention priority (silent/proactive rules first; loud-triggered and
low-frequency ones last), and a short routing list. Mechanics and edge
cases move into on-demand references/ (loaded only when relevant).

References (named with the lark-shared- prefix, matching the per-domain
convention):
- lark-shared-auth-split-flow.md     split-flow steps (marked must-read)
- lark-shared-high-risk-approval.md  exit-10 envelope forms + predict/preview
- lark-shared-identity-and-permissions.md  identity model + scope recovery
- lark-shared-config-init.md         first-run config (blocking, no split-flow)
- lark-shared-update-notice.md       _notice handling (update/skills/deprecated)

Fix doc-vs-implementation drift confirmed against the code:
- exit-10 keys on exit code 10, not the type string; covers both the flat
  (type=confirmation_required) and typed (type=confirmation + subtype)
  envelopes, and reads the confirm flag from hint (--yes / --force).
- distinguish permission_violations (raw API) vs missing_scopes (CLI error).
- complete _notice keys (update / skills / deprecated_command).
- identity failure is silent-or-loud per command, not always empty.

Switch description to Chinese; bump version 1.0.0 -> 1.1.0.

Change-Id: I2dff478ecdc05a13f2d750944f637ed2374961e7
Change-Id: I4068d821adf1b249d00950a8f6ff53414825aeff
Regenerate lark-im SKILL.md (4253 -> 2040 tokens, tiktoken cl100k_base, -52%)
via per-domain gen-skills flags. Drop the scope permission table and the
duplicated schema-usage note, compact API Resources to a terse identity index,
render the Shortcuts table as one-line routing, and move Shortcuts ahead of the
concept sections (executable-first). Full detail stays in --help / schema (the
source of truth); Go Desc and --help are unchanged. Add the NOT boundary, a
good/bad sender example, Flag-type rationale, and anti-hallucination guidance
with a safe fallback (guide users to the Feishu client; do not auto-execute
high-risk raw-API writes).

The master template is variabilized with defaults preserving current output, so
other domains regenerate byte-identical (verified).

Eval (skillave, IM, 9 cases x 3 runs, lark-cli-eval-analyze two-table compare):
mean pass_rate 0.939 -> 0.969, duration -22%, tool-calls -26%, zero structural
regression; anti-hallucination case 0.58 -> 1.00.

Depends on larksuite-cli-registry change (skill-meta.yaml per-domain flags +
gen-skills.py); must merge together with (or before) this PR.

Change-Id: I33c62664388542bfd8ba85ea06c1c5a493c0d935
Drop the redundant Feed-pin parenthetical from the SKILL.md frontmatter description so it fits the non-waivable <=200-token skill-quality gate (206 -> 194, cl100k_base). Behavior-neutral: only the WHEN-routing copy is shortened; skill body and commands are unchanged. Mirrors the same one-line trim in larksuite-cli-registry skill-meta.yaml (im.description).
Change-Id: I40c3d0429b1352949b5b09a79c5700b26fca955d
Squashed net diff of PR #1426 (github-base-token-improve): slim
lark-base SKILL.md + formula/workflow references, split the workflow
guide/schema into per-step references, add guess-tolerant flag aliases
and misuse hints for base record shortcuts (with containment-based
suggest), and drive import adjustments.

Combined onto eval/skills-combined alongside #1450 #1389 #1395 #1410.

Change-Id: Iab1f0c4f1a4c93fd9dbd49bc702cb0ef41022bda
…s structure

- references (im/vc/minutes/whiteboard): drop key catalogs, field/scope tables
  and other content already emitted by `event list`/`event schema`; keep only
  jq recipes, domain semantic gotchas, required params, and per-key identity.
  ~55% token cut across the four references (4343 -> 1940, cl100k_base).
- vc: document the 3 previously-undocumented recording keys, then replace the
  hardcoded key catalog with a pointer to `event list` to stop doc-vs-code drift.
- SKILL.md Topic index: reword rows to advertise recipes/gotchas and route
  structure lookups to the CLI, matching the slimmed references.

Validated by a command-correctness eval (skill+CLI available): 21/21 across the
four domains, identity correct per key, whiteboard required `-p whiteboard_id`
preserved; the VC recording-transcript key (previously unanswerable from the
skill alone) now resolves.

Change-Id: I666e9706ae6ef3e2bbcd56a3fb70c4f8be94182c
@coderabbitai

coderabbitai Bot commented Jun 13, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This PR implements batch field listing (+field-list-batch), adds comprehensive flag aliasing with validation across record/search operations, and restructures skill documentation with 30+ new workflow step references. Code changes include field resolution helpers, plural flag normalization, and improved validation; documentation updates consolidate operational guidance and reorganize guides to emphasize recipes and gotchas over long-form examples.

Changes

Unified Field Operations and Flag Improvements with Documentation Restructure

Layer / File(s) Summary
Field batch listing and compact output
shortcuts/base/field_list_batch.go, shortcuts/base/field_list.go, shortcuts/base/field_ops.go, shortcuts/base/shortcuts.go, shortcuts/base/table_ops.go, skills/lark-base/references/dashboard-block-data-config.md
Adds BaseFieldListBatch shortcut for fetching multiple table fields in one call; refactors executeFieldList to resolve table references and support compact projection; implements listEveryTable pagination helper; updates documentation to recommend batching field fetches.
Field search options validation and aliasing
shortcuts/base/field_search_options.go, shortcuts/base/field_ops.go, shortcuts/base/helpers_test.go
Adds validation requiring field reference, treats --query as deprecated alias for --keyword, introduces field-name alias, refactors search execution to use derived reference/keyword helpers.
Record operations flag aliasing and plural normalization
shortcuts/base/record_ops.go, shortcuts/base/record_query.go, shortcuts/base/record_list.go, shortcuts/base/record_get.go, shortcuts/base/record_search.go, shortcuts/base/helpers_test.go
Implements flag aliasing for record-id(s), field names, and filter/sort JSON with deprecated aliases; adds plural normalization supporting JSON arrays and comma-separated lists; refactors record selection to use merged flag helpers.
Record search query/keyword aliasing
shortcuts/base/record_query.go, shortcuts/base/base_shortcuts_test.go, shortcuts/base/helpers_test.go
Adds --query as deprecated alias for --keyword; implements recordSearchKeyword fallback; validates mutual exclusivity; updates JSON-exclusive checks to use effective keyword.
Suggestion ranking with substring containment
internal/suggest/suggest.go
Extends shortcut suggestion ranking to consider substring containment alongside prefix/edit-distance; adds containsSegment helper.
Table/data query validation and pagination
shortcuts/base/base_data_query.go, shortcuts/base/table_ops.go
Adds --table-id hidden flag to +data-query with validation; implements listEveryTable pagination helper.
Drive import verification output
shortcuts/drive/drive_import.go, shortcuts/drive/drive_import_test.go
Adds execution-time output fields (target_token, verification_token, verification_url, verify_hint) when --target-token provided; updates flag documentation and adds integration test.
Test coverage for new features
shortcuts/base/base_dryrun_ops_test.go, shortcuts/base/base_execute_test.go, shortcuts/base/base_shortcuts_test.go, shortcuts/base/helpers_test.go
Covers field batch dry-run, field/record resolution, record search alias, record list fields alias, field search validation, plural normalization, and flag merge/de-duplication.
Dashboard hidden flag
shortcuts/base/dashboard_block_get_data.go
Adds hidden dashboard-id flag to BaseDashboardBlockGetData.
Lark-Base workflow documentation restructure
skills/lark-base/references/lark-base-workflow-guide.md, skills/lark-base/references/lark-base-workflow-schema.md
Reorganizes workflow guide from lengthy examples into operation maps with shortest-path tables, step-type selection, common scenarios; consolidates schema entrypoint.
Workflow step references
skills/lark-base/references/workflow-steps/*.md (20+ new files)
Adds comprehensive reference documentation for workflow triggers (AddRecord/Button/ChangeRecord/LarkMessage/Reminder/SetRecord/Timer), actions (AddRecord/Delay/FindRecord/GenerateAiText/HTTPClient/LarkMessage/SetRecord), branches (IfElse/Switch), system (Loop), and common types/refs.
Lark-Base SKILL and references restructure
skills/lark-base/SKILL.md, skills/lark-base/references/lark-base-dashboard.md, skills/lark-base/references/lark-base-dashboard-usecase.md, skills/lark-base/references/lark-base-cell-value.md, skills/lark-base/references/lark-base-field-json.md
Adds glossary/concepts section; consolidates operational notes into subsections; documents share-link URL patterns; adds batch field-fetch recommendations; clarifies write constraints (attachment fields, batch limits, option handling).
Formula guide split and update
skills/lark-base/references/formula-field-guide.md, skills/lark-base/references/formula-functions-extended.md, skills/lark-base/references/formula-examples.md
Splits function reference into common and extended (rare) functions; adds comprehensive formula examples with translation rules.
Event skill guides condensed
skills/lark-event/SKILL.md, skills/lark-event/references/lark-event-im.md, skills/lark-event/references/lark-event-vc.md, skills/lark-event/references/lark-event-minutes.md, skills/lark-event/references/lark-event-whiteboard.md
Restructures to defer schema to CLI, focus on payload gotchas and jq recipes, emphasize --as flags and per-event parameters.
IM skill restructure
skills/lark-im/SKILL.md, skill-template/domains/im.md
Introduces shortcuts-first pattern, condenses concepts, rewrites important notes sections, compacts API resources to resource-method hierarchy.
Mail skill tightened
skills/lark-mail/SKILL.md
Emphasizes -h as primary reference, condenses operations, introduces raw API discovery procedure without retained resource tables.
Shared skill updated
skills/lark-shared/SKILL.md, skills/lark-shared/references/*.md (5 new files)
Version bumped to 1.1.0; replaces long-form guide with concise rules linked to reference docs (auth split-flow, config init, high-risk approval, identity/permissions, update notice).
Task skill updated
skills/lark-task/SKILL.md, skills/lark-task/references/lark-task-complete.md, skills/lark-task/references/lark-task-create.md
Adds task-in-tasklist resolution and assignee disambiguation rules; expands workflows with detailed resolution logic; removes permissions table.
Drive and template updates
skills/lark-drive/references/lark-drive-import.md, skill-template/skill-template.md
Clarifies verification-token usage; adds skill template placeholders for dynamic schema/permission content.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • larksuite/cli#505: Modifies skills/lark-base/SKILL.md's identity and permission handling guidance with user-first strategy and 91403 error handling.

Suggested labels

domain/ccm, feature, size/XL

Suggested reviewers

  • zhaoleibd
  • fangshuyu-768
  • kongenpei
  • wittam-01

🐰 Batch fields in one swift hop,
Aliases fold, but queries don't stop—
Workflows mapped from schemas so deep,
With references stacked in a helpful heap!
📚✨

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch eval/skills-combined

@github-actions github-actions Bot added domain/base PR touches the base domain domain/ccm PR touches the ccm domain domain/im PR touches the im domain domain/mail PR touches the mail domain domain/task PR touches the task domain size/XL Architecture-level or global-impact change labels Jun 13, 2026
@codecov

codecov Bot commented Jun 13, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 82.75862% with 40 lines in your changes missing coverage. Please review.
✅ Project coverage is 73.34%. Comparing base (0fbfe68) to head (cbd7297).
⚠️ Report is 31 commits behind head on main.

Files with missing lines Patch % Lines
shortcuts/base/field_ops.go 78.07% 13 Missing and 12 partials ⚠️
shortcuts/base/record_query.go 77.77% 3 Missing and 3 partials ⚠️
shortcuts/base/table_ops.go 80.00% 2 Missing and 1 partial ⚠️
internal/suggest/suggest.go 86.66% 1 Missing and 1 partial ⚠️
shortcuts/base/base_data_query.go 0.00% 2 Missing ⚠️
shortcuts/base/record_list.go 0.00% 1 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1452      +/-   ##
==========================================
+ Coverage   72.88%   73.34%   +0.45%     
==========================================
  Files         738      751      +13     
  Lines       69553    69450     -103     
==========================================
+ Hits        50695    50939     +244     
+ Misses      15043    14755     -288     
+ Partials     3815     3756      -59     

☔ 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

Copy link
Copy Markdown

🚀 PR Preview Install Guide

🧰 CLI update

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

🧩 Skill update

npx skills add larksuite/cli#eval/skills-combined -y -g

@liangshuo-1 liangshuo-1 changed the title eval: combined skill optimizations (#1450 #1389 #1395 #1410 #1426 #1451) docs(skills): slim SKILL.md context and token usage across domains Jun 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 (7)
internal/suggest/suggest.go (1)

107-120: ⚡ Quick win

Eliminate repeated rune conversions.

len([]rune(a)) and len([]rune(b)) are each called multiple times (lines 116, 119), allocating new rune slices on every call. Compute the rune slices once and reuse them.

♻️ Proposed refactor
 func containsSegment(a, b string) bool {
 	a = strings.TrimLeft(a, "+-")
 	b = strings.TrimLeft(b, "+-")
+	ra, rb := []rune(a), []rune(b)
-	if len([]rune(a)) > len([]rune(b)) {
+	if len(ra) > len(rb) {
 		a, b = b, a
+		ra, rb = rb, ra
 	}
-	return len([]rune(a)) >= 5 && strings.Contains(b, a)
+	return len(ra) >= 5 && strings.Contains(b, a)
 }
🤖 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/suggest/suggest.go` around lines 107 - 120, The containsSegment
function repeatedly converts strings to []rune causing allocations; modify
containsSegment to compute runesA := []rune(a) and runesB := []rune(b) once
(after trimming "+/-"), use len(runesA) and len(runesB) and strings.Contains on
the original b/a strings as appropriate, and swap runes (and the original string
variables if you prefer) when you need to ensure runesA is the shorter side so
the subsequent length check and containment use the precomputed rune lengths
instead of calling len([]rune(...)) multiple times.
skills/lark-base/references/lark-base-workflow-guide.md (2)

82-83: ⚡ Quick win

Clarify the ChangeRecordTrigger guidance.

Lines 82–83 recommend using a single ChangeRecordTrigger with condition_list for scenarios where "修改为 X 新增 X" (modify to X or create as X). This is a significant design choice that differs from the naive "create two workflows" approach.

This guidance is consistent with lark-base-workflow-schema.md line 67, which restates the same rule. However, to help users trust and apply this guidance, consider adding a brief example or explanation of why ChangeRecordTrigger + condition_list is superior (e.g., "single entry point, simpler maintenance, explicit intent").

🤖 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-base/references/lark-base-workflow-guide.md` around lines 82 -
83, Update the guidance around ChangeRecordTrigger to explicitly recommend a
single ChangeRecordTrigger with condition_list for "修改为 X 或 新增 X" scenarios:
state that using ChangeRecordTrigger plus condition_list provides a single entry
point, simpler maintenance, and clearer intent versus separate AddRecordTrigger
and SetRecordTrigger workflows, and add a concise example description showing
condition_list matching both "modify to X" and "create as X" cases (reference
ChangeRecordTrigger and condition_list).

7-14: ⚡ Quick win

Consolidate read-order and decision-tree guidance across guide and schema.

Both lark-base-workflow-guide.md (lines 7–14, "先判断任务类型" table) and lark-base-workflow-schema.md (lines 5–10, "读取顺序") describe the decision path for when to consult which resource. The concepts are similar but phrased differently:

  • Guide: Task-type table (list/get/enable/modify/create/explain) with "是否读 schema" column
  • Schema: Numbered read-order steps (1. query/enable/disable, 2. create/update, 3. determine step types, 4. add common-types-and-refs)

While both are useful (guide for task lookup, schema for workflow construction), there is some potential for users to feel confused about which guidance to follow first.

Consider adding a brief sentence at the top of each file clarifying the intended entry point:

  • Guide: "Start here if you know what task you want to perform (list/modify/create/etc.)"
  • Schema: "Once in guide, use this for detailed JSON structure and step type reference"

This would prevent the perception of duplication and make the division of labor clearer.

🤖 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-base/references/lark-base-workflow-guide.md` around lines 7 - 14,
Add a one-line clarifying entry sentence to each file: in
skills/lark-base/references/lark-base-workflow-guide.md (lines 7-14) prepend the
table with "Start here if you know what task you want to perform
(list/modify/create/etc.)." and in
skills/lark-base/references/lark-base-workflow-schema.md (lines 5-10) prepend
the numbered read-order with "Use this after consulting the guide above for
detailed JSON structure and step-type reference." — make only those single-line
insertions so the guide remains the task-first entry point and the schema
remains the detailed, follow-up reference.
skills/lark-base/references/lark-base-workflow-schema.md (1)

33-33: ⚡ Quick win

Clarify the "连线/扩展/输入" principle.

Line 33 states: "总原则:连线写 children,扩展标识写 meta,输入参数写 data."

This is a good organizing principle, but the guide shows JSON examples in lark-base-workflow-guide.md lines 104–133 that contain only id, type, title, next, and data. There is no meta field visible in the minimal example or in any of the 6 provided action step files.

Please clarify:

  1. Is meta currently in use in any step type, or is it a placeholder for future extension?
  2. Should users see meta in their examples, or is it optional/implementation-internal?

Adding a brief note here (e.g., "Note: meta is reserved for future extension; most workflows do not use it") would reduce confusion.

🤖 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-base/references/lark-base-workflow-schema.md` at line 33, Add a
one-sentence clarification under the principle line stating that `meta` is
reserved for extensions and is optional/implementation-internal (most existing
step types and the minimal examples only use `id`, `type`, `title`, `next`,
`data`, and do not include `meta`), and optionally note that if a step type uses
`meta` it will be documented in that step's spec; update the sentence near
"总原则:连线写 `children`,扩展标识写 `meta`,输入参数写 `data`" to include this brief note.
skills/lark-base/references/workflow-steps/trigger-lark-message.md (1)

1-40: ⚖️ Poor tradeoff

All four trigger reference pages omit output documentation—critical for workflow authors needing to reference trigger outputs downstream.

Each of these trigger references documents configuration inputs completely but lacks an outputs section defining the variables available for subsequent steps. The upstream contracts in common-types-and-refs.md define these outputs; they should be reflected in each trigger's reference page for discoverability and completeness.

  • trigger-lark-message.md#L1-L40: Add outputs section documenting Sender, AtUser, SenderGroup, MessageSendTime, MessageContent, MessageType, MessageID, MessageLink, ParentID, ThreadID, Attachments with constraint that SenderGroup/MessageLink are unavailable for single-chat.
  • trigger-reminder.md#L1-L30: Add outputs section documenting shared record trigger outputs: {fieldId} (with sub-properties), startTime, recordId, recordLink, recordCreatedUser, recordCreatedTime, recordModifiedUser, recordModifiedTime, plus dynamic field outputs.
  • trigger-set-record.md#L1-L38: Add outputs section documenting shared record trigger outputs (same as ReminderTrigger).
  • trigger-timer.md#L1-L27: Add outputs section documenting scheduleTime available output.

Verification needed: Confirm whether output documentation should be added to each trigger file (recommended for user discoverability) or whether centralization in common-types-and-refs.md is the intended design. If per-file outputs are preferred, generate diffs for all four files with appropriate output sections based on the upstream contract definitions.

🤖 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-base/references/workflow-steps/trigger-lark-message.md` around
lines 1 - 40, Add per-trigger "Outputs" documentation sections to surface
downstream variables defined by the upstream contracts instead of relying only
on common-types-and-refs.md: in
skills/lark-base/references/workflow-steps/trigger-lark-message.md (lines 1-40)
insert an Outputs section documenting Sender, AtUser, SenderGroup (note:
SenderGroup and MessageLink not present for single-chat), MessageSendTime,
MessageContent, MessageType, MessageID, MessageLink, ParentID, ThreadID,
Attachments; in skills/lark-base/references/workflow-steps/trigger-reminder.md
(lines 1-30) add Outputs documenting shared record trigger outputs: each dynamic
{fieldId} with sub-properties (id, value, fieldType), startTime, recordId,
recordLink, recordCreatedUser, recordCreatedTime, recordModifiedUser,
recordModifiedTime and mention dynamic field outputs; in
skills/lark-base/references/workflow-steps/trigger-set-record.md (lines 1-38)
add the same shared record Outputs as ReminderTrigger; and in
skills/lark-base/references/workflow-steps/trigger-timer.md (lines 1-27) add an
Outputs section documenting scheduleTime; ensure each new section briefly states
types and any chat-scene constraints and references common-types-and-refs.md for
full schemas.
shortcuts/base/record_ops.go (1)

53-53: 💤 Low value

Consider improving error message readability.

The error message --record-id/--record-ids and --json are mutually exclusive could be more readable as --record-id / --record-ids and --json are mutually exclusive (with spaces around the slash).

🤖 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 `@shortcuts/base/record_ops.go` at line 53, The error message passed to
baseFlagErrorf in the record selection code should be made more readable by
adding spaces around the slash; update the string in the return that constructs
the error (the call to baseFlagErrorf that currently reads
"--record-id/--record-ids and --json are mutually exclusive") to "--record-id /
--record-ids and --json are mutually exclusive" (the surrounding code returning
recordSelection{} and the baseFlagErrorf call remain the same).
shortcuts/base/dashboard_block_get_data.go (1)

22-22: ⚡ Quick win

Hidden flag lacks validation, creating inconsistency with similar pattern.

Unlike BaseDataQuery (which adds a hidden table-id flag and explicitly rejects it in Validate), this command registers a hidden dashboard-id flag but does not validate or reject it. The flag is never referenced in DryRun or Execute, so it would be silently ignored if provided.

Since the Tips (Line 27) state "This command does not need --dashboard-id", consider either:

  1. Adding validation to reject --dashboard-id with a helpful error (matching the base_data_query.go pattern), or
  2. Removing the flag entirely if it serves no purpose.
🤖 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 `@shortcuts/base/dashboard_block_get_data.go` at line 22, The hidden flag
"dashboard-id" (registered as {Name: "dashboard-id", Hidden: true}) is never
validated and will be silently ignored; follow the BaseDataQuery pattern by
updating the command's Validate method to detect when the user passed
--dashboard-id and return a clear error (e.g., "this command does not accept
--dashboard-id"), so that Validate rejects the flag rather than ignoring it;
alternatively remove the hidden flag registration if you prefer that
behavior—apply the change in the command's Validate implementation (and keep
DryRun/Execute unchanged).
🤖 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 `@internal/suggest/suggest.go`:
- Around line 107-120: Add a new table-driven unit test in
internal/suggest/suggest_test.go that directly exercises containsSegment: create
cases for (1) sigil-trimmed containment (e.g. "+block-list" vs
"+base-block-list" should return true), (2) the minimum-length guard (a short
fragment like "list" vs "+base-list" should return false because it’s < 5
runes), and (3) a non-containment case that returns false; iterate the table,
call containsSegment(a,b) and assert expected boolean for each case so the sigil
stripping, rune-length check, and substring logic are covered.

In `@shortcuts/base/base_execute_test.go`:
- Around line 1069-1191: Add a new unit test in
shortcuts/base/base_execute_test.go that exercises the single-table compact
branch for +field-list: use newExecuteFactory, register an httpmock.Stub for GET
/.../tables/<tbl>/fields returning a field with "options" containing extra keys
(e.g. name and color), call runShortcut with BaseFieldList and args including
"--compact" and the table id, and assert the output retains the option "name"
but omits extra option fields (e.g. no "color"); this will cover the compact
projection logic added in the single-list branch in field_ops.go (the code path
invoked by BaseFieldList with "--compact").

In `@shortcuts/base/base_shortcuts_test.go`:
- Around line 72-77: Replace substring-only error assertions with typed-error
metadata and cause checks: for shortcuts/base/base_shortcuts_test.go (lines
72-77) change the check of BaseFieldSearchOptions.Validate to use
errs.ProblemOf(err, "validation", "required", "field-id") (or the equivalent
project helper) and assert the Param by doing errors.As(err,
*errs.ValidationError) to inspect Param == "field-id", and also assert the
wrapped cause if applicable; for shortcuts/base/base_shortcuts_test.go (lines
1116-1123) replace the `"use only one"` substring assertion with errs.ProblemOf
checking the appropriate category/subtype/param for the mutually-exclusive alias
error and use errors.As to verify the ValidationError.Param and any cause; for
shortcuts/base/helpers_test.go (lines 545-549) replace the keyword+query
conflict substring check with errs.ProblemOf for the conflict error, use
errors.As to assert the ValidationError.Param equals "keyword" (or the correct
param name), and verify the original cause is preserved via errors.Unwrap /
errors.Is as appropriate. Use the symbols BaseFieldSearchOptions.Validate,
errs.ProblemOf, and *errs.ValidationError in your changes.

In `@shortcuts/base/field_ops.go`:
- Around line 244-246: isBaseTableID currently returns true for any ref that
simply starts with "tbl", causing names like "tbl Orders" to be misclassified;
update isBaseTableID to perform a stricter shape check (for example match a
well-defined ID regex such as ^tbl_[A-Za-z0-9-]+$ or ^tbl:[A-Za-z0-9]+$) and
return false for anything that doesn't match so the normal resolution path is
used as a fallback; ensure the function name is kept (isBaseTableID) so callers
continue to use it and test with example inputs like "tbl123", "tbl_abc-1", and
"tbl Orders" to verify behavior.

In `@shortcuts/base/table_ops.go`:
- Around line 190-201: The pagination break logic in listEveryTable is
incorrectly using the returned total even when listAllTables falls back to the
current page size, causing early termination; update the condition so total is
only treated as authoritative when it exceeds the current batch size — i.e.,
change the break clause to use (total > len(batch) && len(items) >= total)
instead of (total > 0 && len(items) >= total), leaving the other checks
(len(batch) == 0 || len(batch) < pageLimit) unchanged; this ensures
listEveryTable and its use of listAllTables correctly page through all tables
for name-based resolution.

In `@skills/lark-base/references/workflow-steps/trigger-change-record.md`:
- Around line 5-11: The JSON example uses the wrong field name "condition" —
update the example in trigger-change-record.md so the payload uses
"condition_list" (e.g., "condition_list": null or an appropriate array) instead
of "condition" and ensure the example matches the field table; verify the JSON
block containing "table_name", "trigger_control_list" and the condition field is
corrected to use "condition_list".

---

Nitpick comments:
In `@internal/suggest/suggest.go`:
- Around line 107-120: The containsSegment function repeatedly converts strings
to []rune causing allocations; modify containsSegment to compute runesA :=
[]rune(a) and runesB := []rune(b) once (after trimming "+/-"), use len(runesA)
and len(runesB) and strings.Contains on the original b/a strings as appropriate,
and swap runes (and the original string variables if you prefer) when you need
to ensure runesA is the shorter side so the subsequent length check and
containment use the precomputed rune lengths instead of calling len([]rune(...))
multiple times.

In `@shortcuts/base/dashboard_block_get_data.go`:
- Line 22: The hidden flag "dashboard-id" (registered as {Name: "dashboard-id",
Hidden: true}) is never validated and will be silently ignored; follow the
BaseDataQuery pattern by updating the command's Validate method to detect when
the user passed --dashboard-id and return a clear error (e.g., "this command
does not accept --dashboard-id"), so that Validate rejects the flag rather than
ignoring it; alternatively remove the hidden flag registration if you prefer
that behavior—apply the change in the command's Validate implementation (and
keep DryRun/Execute unchanged).

In `@shortcuts/base/record_ops.go`:
- Line 53: The error message passed to baseFlagErrorf in the record selection
code should be made more readable by adding spaces around the slash; update the
string in the return that constructs the error (the call to baseFlagErrorf that
currently reads "--record-id/--record-ids and --json are mutually exclusive") to
"--record-id / --record-ids and --json are mutually exclusive" (the surrounding
code returning recordSelection{} and the baseFlagErrorf call remain the same).

In `@skills/lark-base/references/lark-base-workflow-guide.md`:
- Around line 82-83: Update the guidance around ChangeRecordTrigger to
explicitly recommend a single ChangeRecordTrigger with condition_list for "修改为 X
或 新增 X" scenarios: state that using ChangeRecordTrigger plus condition_list
provides a single entry point, simpler maintenance, and clearer intent versus
separate AddRecordTrigger and SetRecordTrigger workflows, and add a concise
example description showing condition_list matching both "modify to X" and
"create as X" cases (reference ChangeRecordTrigger and condition_list).
- Around line 7-14: Add a one-line clarifying entry sentence to each file: in
skills/lark-base/references/lark-base-workflow-guide.md (lines 7-14) prepend the
table with "Start here if you know what task you want to perform
(list/modify/create/etc.)." and in
skills/lark-base/references/lark-base-workflow-schema.md (lines 5-10) prepend
the numbered read-order with "Use this after consulting the guide above for
detailed JSON structure and step-type reference." — make only those single-line
insertions so the guide remains the task-first entry point and the schema
remains the detailed, follow-up reference.

In `@skills/lark-base/references/lark-base-workflow-schema.md`:
- Line 33: Add a one-sentence clarification under the principle line stating
that `meta` is reserved for extensions and is optional/implementation-internal
(most existing step types and the minimal examples only use `id`, `type`,
`title`, `next`, `data`, and do not include `meta`), and optionally note that if
a step type uses `meta` it will be documented in that step's spec; update the
sentence near "总原则:连线写 `children`,扩展标识写 `meta`,输入参数写 `data`" to include this
brief note.

In `@skills/lark-base/references/workflow-steps/trigger-lark-message.md`:
- Around line 1-40: Add per-trigger "Outputs" documentation sections to surface
downstream variables defined by the upstream contracts instead of relying only
on common-types-and-refs.md: in
skills/lark-base/references/workflow-steps/trigger-lark-message.md (lines 1-40)
insert an Outputs section documenting Sender, AtUser, SenderGroup (note:
SenderGroup and MessageLink not present for single-chat), MessageSendTime,
MessageContent, MessageType, MessageID, MessageLink, ParentID, ThreadID,
Attachments; in skills/lark-base/references/workflow-steps/trigger-reminder.md
(lines 1-30) add Outputs documenting shared record trigger outputs: each dynamic
{fieldId} with sub-properties (id, value, fieldType), startTime, recordId,
recordLink, recordCreatedUser, recordCreatedTime, recordModifiedUser,
recordModifiedTime and mention dynamic field outputs; in
skills/lark-base/references/workflow-steps/trigger-set-record.md (lines 1-38)
add the same shared record Outputs as ReminderTrigger; and in
skills/lark-base/references/workflow-steps/trigger-timer.md (lines 1-27) add an
Outputs section documenting scheduleTime; ensure each new section briefly states
types and any chat-scene constraints and references common-types-and-refs.md for
full schemas.
🪄 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: b83fcce8-0697-4f50-a530-2a7d2266815c

📥 Commits

Reviewing files that changed from the base of the PR and between deb0bd9 and cbd7297.

📒 Files selected for processing (68)
  • internal/suggest/suggest.go
  • shortcuts/base/base_data_query.go
  • shortcuts/base/base_dryrun_ops_test.go
  • shortcuts/base/base_execute_test.go
  • shortcuts/base/base_shortcuts_test.go
  • shortcuts/base/dashboard_block_get_data.go
  • shortcuts/base/field_list.go
  • shortcuts/base/field_list_batch.go
  • shortcuts/base/field_ops.go
  • shortcuts/base/field_search_options.go
  • shortcuts/base/helpers_test.go
  • shortcuts/base/record_get.go
  • shortcuts/base/record_list.go
  • shortcuts/base/record_ops.go
  • shortcuts/base/record_query.go
  • shortcuts/base/record_search.go
  • shortcuts/base/shortcuts.go
  • shortcuts/base/table_ops.go
  • shortcuts/drive/drive_import.go
  • shortcuts/drive/drive_import_test.go
  • skill-template/domains/im.md
  • skill-template/skill-template.md
  • skills/lark-base/SKILL.md
  • skills/lark-base/references/dashboard-block-data-config.md
  • skills/lark-base/references/formula-examples.md
  • skills/lark-base/references/formula-field-guide.md
  • skills/lark-base/references/formula-functions-extended.md
  • skills/lark-base/references/lark-base-cell-value.md
  • skills/lark-base/references/lark-base-dashboard-usecase.md
  • skills/lark-base/references/lark-base-dashboard.md
  • skills/lark-base/references/lark-base-field-json.md
  • skills/lark-base/references/lark-base-workflow-guide.md
  • skills/lark-base/references/lark-base-workflow-schema.md
  • skills/lark-base/references/workflow-steps/action-add-record.md
  • skills/lark-base/references/workflow-steps/action-delay.md
  • skills/lark-base/references/workflow-steps/action-find-record.md
  • skills/lark-base/references/workflow-steps/action-generate-ai-text.md
  • skills/lark-base/references/workflow-steps/action-http-client.md
  • skills/lark-base/references/workflow-steps/action-lark-message.md
  • skills/lark-base/references/workflow-steps/action-set-record.md
  • skills/lark-base/references/workflow-steps/branch-if-else.md
  • skills/lark-base/references/workflow-steps/branch-switch.md
  • skills/lark-base/references/workflow-steps/common-types-and-refs.md
  • skills/lark-base/references/workflow-steps/system-loop.md
  • skills/lark-base/references/workflow-steps/trigger-add-record.md
  • skills/lark-base/references/workflow-steps/trigger-button.md
  • skills/lark-base/references/workflow-steps/trigger-change-record.md
  • skills/lark-base/references/workflow-steps/trigger-lark-message.md
  • skills/lark-base/references/workflow-steps/trigger-reminder.md
  • skills/lark-base/references/workflow-steps/trigger-set-record.md
  • skills/lark-base/references/workflow-steps/trigger-timer.md
  • skills/lark-drive/references/lark-drive-import.md
  • skills/lark-event/SKILL.md
  • skills/lark-event/references/lark-event-im.md
  • skills/lark-event/references/lark-event-minutes.md
  • skills/lark-event/references/lark-event-vc.md
  • skills/lark-event/references/lark-event-whiteboard.md
  • skills/lark-im/SKILL.md
  • skills/lark-mail/SKILL.md
  • skills/lark-shared/SKILL.md
  • skills/lark-shared/references/lark-shared-auth-split-flow.md
  • skills/lark-shared/references/lark-shared-config-init.md
  • skills/lark-shared/references/lark-shared-high-risk-approval.md
  • skills/lark-shared/references/lark-shared-identity-and-permissions.md
  • skills/lark-shared/references/lark-shared-update-notice.md
  • skills/lark-task/SKILL.md
  • skills/lark-task/references/lark-task-complete.md
  • skills/lark-task/references/lark-task-create.md

Comment on lines +107 to +120
// containsSegment reports whether one name contains the other as a substring
// after stripping the "+"/"--" sigils. It catches hallucinated names that drop
// a namespace prefix (e.g. "+block-list" for "+base-block-list"), which share
// almost no prefix and sit far beyond the edit-distance budget. The shorter
// side must be at least 5 runes so generic fragments like "list" do not match
// half the catalog.
func containsSegment(a, b string) bool {
a = strings.TrimLeft(a, "+-")
b = strings.TrimLeft(b, "+-")
if len([]rune(a)) > len([]rune(b)) {
a, b = b, a
}
return len([]rune(a)) >= 5 && strings.Contains(b, a)
}

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Check for tests covering containsSegment.

# Test: Search for containsSegment in test files.
rg -n 'containsSegment' --glob '*_test.go'

Repository: larksuite/cli

Length of output: 39


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# 1) Find call sites
echo "== Call sites in suggest.go =="
rg -n 'containsSegment\(' internal/suggest/suggest.go

# 2) Locate the specific test and show surrounding content
echo "== TestClosest_HallucinatedSharesPrefix in suggest_test.go =="
rg -n 'TestClosest_HallucinatedSharesPrefix' internal/suggest/suggest_test.go
sed -n '1,220p' internal/suggest/suggest_test.go | sed -n '/TestClosest_HallucinatedSharesPrefix/,$p' | head -n 200

Repository: larksuite/cli

Length of output: 2347


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== suggest.go around containsSegment call =="
sed -n '1,200p' internal/suggest/suggest.go | sed -n '40,160p'

echo "== Remaining tests in suggest_test.go (beyond first 220 lines if any) =="
wc -l internal/suggest/suggest_test.go
sed -n '1,260p' internal/suggest/suggest_test.go

Repository: larksuite/cli

Length of output: 5267


Add direct unit tests for containsSegment

internal/suggest/suggest_test.go has no tests that exercise containsSegment itself (no coverage for the sigil-trimmed substring logic or the “>= 5 runes” minimum), so cases like "+block-list" vs "+base-block-list" are not asserted. Add a table-driven test for containsSegment covering: containment after trimming ("+block-list" / "+base-block-list"), the minimum-length guard (reject short fragments like "list"), and non-containment.

🤖 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/suggest/suggest.go` around lines 107 - 120, Add a new table-driven
unit test in internal/suggest/suggest_test.go that directly exercises
containsSegment: create cases for (1) sigil-trimmed containment (e.g.
"+block-list" vs "+base-block-list" should return true), (2) the minimum-length
guard (a short fragment like "list" vs "+base-list" should return false because
it’s < 5 runes), and (3) a non-containment case that returns false; iterate the
table, call containsSegment(a,b) and assert expected boolean for each case so
the sigil stripping, rune-length check, and substring logic are covered.

Comment on lines +1069 to +1191
t.Run("list resolves table name", func(t *testing.T) {
factory, stdout, reg := newExecuteFactory(t)
reg.Register(&httpmock.Stub{
Method: "GET",
URL: "/open-apis/base/v3/bases/app_x/tables",
Body: map[string]interface{}{
"code": 0,
"data": map[string]interface{}{"tables": []interface{}{
map[string]interface{}{"id": "tbl_orders", "name": "Orders"},
}, "total": 1},
},
})
reg.Register(&httpmock.Stub{
Method: "GET",
URL: "/open-apis/base/v3/bases/app_x/tables/tbl_orders/fields",
Body: map[string]interface{}{
"code": 0,
"data": map[string]interface{}{"fields": []interface{}{
map[string]interface{}{"id": "fld_name", "name": "Name", "type": "text"},
}, "total": 1},
},
})
if err := runShortcut(t, BaseFieldList, []string{"+field-list", "--base-token", "app_x", "--table-id", "Orders"}, factory, stdout); err != nil {
t.Fatalf("err=%v", err)
}
if got := stdout.String(); !strings.Contains(got, `"fields"`) || !strings.Contains(got, `"name": "Name"`) {
t.Fatalf("stdout=%s", got)
}
})

t.Run("list batch multiple tables", func(t *testing.T) {
factory, stdout, reg := newExecuteFactory(t)
reg.Register(&httpmock.Stub{
Method: "GET",
URL: "/open-apis/base/v3/bases/app_x/tables/tbl_a/fields",
Body: map[string]interface{}{
"code": 0,
"data": map[string]interface{}{"fields": []interface{}{
map[string]interface{}{"id": "fld_a", "name": "Name", "type": "text", "style": map[string]interface{}{"type": "plain"}},
}, "total": 1},
},
})
reg.Register(&httpmock.Stub{
Method: "GET",
URL: "/open-apis/base/v3/bases/app_x/tables/tbl_b/fields",
Body: map[string]interface{}{
"code": 0,
"data": map[string]interface{}{"fields": []interface{}{
map[string]interface{}{"id": "fld_b", "name": "Status", "type": "select", "options": []interface{}{map[string]interface{}{"name": "Todo", "color": "red"}}},
}, "total": 1},
},
})
if err := runShortcut(t, BaseFieldListBatch, []string{"+field-list-batch", "--base-token", "app_x", "--table-id", "tbl_a", "--table-id", "tbl_b", "--compact"}, factory, stdout); err != nil {
t.Fatalf("err=%v", err)
}
got := stdout.String()
if !strings.Contains(got, `"tables"`) || !strings.Contains(got, `"table_id": "tbl_a"`) || !strings.Contains(got, `"table_id": "tbl_b"`) || !strings.Contains(got, `"options": [`) || !strings.Contains(got, `"Todo"`) || !strings.Contains(got, `"style"`) || strings.Contains(got, `"color"`) {
t.Fatalf("stdout=%s", got)
}
})

t.Run("list batch resolves table names", func(t *testing.T) {
factory, stdout, reg := newExecuteFactory(t)
reg.Register(&httpmock.Stub{
Method: "GET",
URL: "/open-apis/base/v3/bases/app_x/tables",
Body: map[string]interface{}{
"code": 0,
"data": map[string]interface{}{"tables": []interface{}{
map[string]interface{}{"id": "tbl_orders", "name": "Orders"},
}, "total": 1},
},
})
reg.Register(&httpmock.Stub{
Method: "GET",
URL: "/open-apis/base/v3/bases/app_x/tables/tbl_a/fields",
Body: map[string]interface{}{
"code": 0,
"data": map[string]interface{}{"fields": []interface{}{
map[string]interface{}{"id": "fld_a", "name": "Name", "type": "text"},
}, "total": 1},
},
})
reg.Register(&httpmock.Stub{
Method: "GET",
URL: "/open-apis/base/v3/bases/app_x/tables/tbl_orders/fields",
Body: map[string]interface{}{
"code": 0,
"data": map[string]interface{}{"fields": []interface{}{
map[string]interface{}{"id": "fld_order", "name": "Status", "type": "select"},
}, "total": 1},
},
})
if err := runShortcut(t, BaseFieldListBatch, []string{"+field-list-batch", "--base-token", "app_x", "--table-id", "tbl_a", "--table-id", "Orders"}, factory, stdout); err != nil {
t.Fatalf("err=%v", err)
}
got := stdout.String()
if !strings.Contains(got, `"table_id": "tbl_a"`) || !strings.Contains(got, `"table_id": "tbl_orders"`) || !strings.Contains(got, `"table_ref": "Orders"`) || !strings.Contains(got, `"table_name": "Orders"`) {
t.Fatalf("stdout=%s", got)
}
})

t.Run("list batch default keeps full fields", func(t *testing.T) {
factory, stdout, reg := newExecuteFactory(t)
reg.Register(&httpmock.Stub{
Method: "GET",
URL: "/open-apis/base/v3/bases/app_x/tables/tbl_b/fields",
Body: map[string]interface{}{
"code": 0,
"data": map[string]interface{}{"fields": []interface{}{
map[string]interface{}{"id": "fld_b", "name": "Status", "type": "select", "options": []interface{}{map[string]interface{}{"name": "Todo", "color": "red"}}},
}, "total": 1},
},
})
if err := runShortcut(t, BaseFieldListBatch, []string{"+field-list-batch", "--base-token", "app_x", "--table-id", "tbl_b"}, factory, stdout); err != nil {
t.Fatalf("err=%v", err)
}
got := stdout.String()
if !strings.Contains(got, `"table_id": "tbl_b"`) || !strings.Contains(got, `"color": "red"`) {
t.Fatalf("stdout=%s", got)
}
})

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 | ⚡ Quick win

Add direct execute coverage for single-table +field-list --compact.

These additions cover batch compact/default, but the new single-list compact branch (shortcuts/base/field_ops.go, Line 156-158) is still untested in this file. Please add one case asserting projection behavior (for example, select option name retained while extra option fields are omitted).

🧪 Suggested test skeleton
 t.Run("list resolves table name", func(t *testing.T) {
   ...
 })
 
+t.Run("list compact projects field payload", func(t *testing.T) {
+	factory, stdout, reg := newExecuteFactory(t)
+	reg.Register(&httpmock.Stub{
+		Method: "GET",
+		URL:    "/open-apis/base/v3/bases/app_x/tables/tbl_x/fields",
+		Body: map[string]interface{}{
+			"code": 0,
+			"data": map[string]interface{}{"fields": []interface{}{
+				map[string]interface{}{
+					"id": "fld_status", "name": "Status", "type": "select",
+					"options": []interface{}{map[string]interface{}{"name": "Todo", "color": "red"}},
+				},
+			}, "total": 1},
+		},
+	})
+	if err := runShortcut(t, BaseFieldList, []string{"+field-list", "--base-token", "app_x", "--table-id", "tbl_x", "--compact"}, factory, stdout); err != nil {
+		t.Fatalf("err=%v", err)
+	}
+	got := stdout.String()
+	if !strings.Contains(got, `"options": [`) || !strings.Contains(got, `"Todo"`) || strings.Contains(got, `"color"`) {
+		t.Fatalf("stdout=%s", got)
+	}
+})
+
 t.Run("list batch multiple tables", func(t *testing.T) {
   ...
 })
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 `@shortcuts/base/base_execute_test.go` around lines 1069 - 1191, Add a new unit
test in shortcuts/base/base_execute_test.go that exercises the single-table
compact branch for +field-list: use newExecuteFactory, register an httpmock.Stub
for GET /.../tables/<tbl>/fields returning a field with "options" containing
extra keys (e.g. name and color), call runShortcut with BaseFieldList and args
including "--compact" and the table id, and assert the output retains the option
"name" but omits extra option fields (e.g. no "color"); this will cover the
compact projection logic added in the single-list branch in field_ops.go (the
code path invoked by BaseFieldList with "--compact").

Source: Coding guidelines

Comment on lines +72 to +77
func TestFieldSearchOptionsRequiresFieldRef(t *testing.T) {
err := BaseFieldSearchOptions.Validate(context.Background(), newBaseTestRuntime(map[string]string{}, nil, nil))
if err == nil || !strings.Contains(err.Error(), "--field-id is required") {
t.Fatalf("err=%v", 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.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

New error-path tests should assert typed error metadata, not message substrings. The shared root cause is substring-only checks on err.Error(), which can pass even when error classification (category/subtype/param) or cause chaining regresses.

  • shortcuts/base/base_shortcuts_test.go#L72-L77: replace substring matching with errs.ProblemOf assertions and errors.As(..., *errs.ValidationError) for Param, plus cause checks.
  • shortcuts/base/base_shortcuts_test.go#L1116-L1123: assert typed validation metadata for the mutually-exclusive alias failure instead of "use only one" substring.
  • shortcuts/base/helpers_test.go#L545-L549: use typed metadata assertions for the keyword+query conflict path and verify cause propagation.

As per coding guidelines, **/*_test.go: “Error-path tests must assert typed metadata via errs.ProblemOf (category / subtype / param) and cause preservation, not message substrings alone.” Based on learnings, Param must be asserted via errors.As on *errs.ValidationError.

📍 Affects 2 files
  • shortcuts/base/base_shortcuts_test.go#L72-L77 (this comment)
  • shortcuts/base/base_shortcuts_test.go#L1116-L1123
  • shortcuts/base/helpers_test.go#L545-L549
🤖 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 `@shortcuts/base/base_shortcuts_test.go` around lines 72 - 77, Replace
substring-only error assertions with typed-error metadata and cause checks: for
shortcuts/base/base_shortcuts_test.go (lines 72-77) change the check of
BaseFieldSearchOptions.Validate to use errs.ProblemOf(err, "validation",
"required", "field-id") (or the equivalent project helper) and assert the Param
by doing errors.As(err, *errs.ValidationError) to inspect Param == "field-id",
and also assert the wrapped cause if applicable; for
shortcuts/base/base_shortcuts_test.go (lines 1116-1123) replace the `"use only
one"` substring assertion with errs.ProblemOf checking the appropriate
category/subtype/param for the mutually-exclusive alias error and use errors.As
to verify the ValidationError.Param and any cause; for
shortcuts/base/helpers_test.go (lines 545-549) replace the keyword+query
conflict substring check with errs.ProblemOf for the conflict error, use
errors.As to assert the ValidationError.Param equals "keyword" (or the correct
param name), and verify the original cause is preserved via errors.Unwrap /
errors.Is as appropriate. Use the symbols BaseFieldSearchOptions.Validate,
errs.ProblemOf, and *errs.ValidationError in your changes.

Sources: Coding guidelines, Learnings

Comment on lines +244 to +246
func isBaseTableID(ref string) bool {
return strings.HasPrefix(strings.TrimSpace(ref), "tbl")
}

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 | ⚡ Quick win

Harden table-ID detection to avoid false positives on table names.

Line 245 treats any ref starting with tbl as an ID. Names like tbl Orders will bypass resolution and fail later API calls (for example at Line 176 / Line 149). Use a stricter ID shape check (or fallback resolution) instead of prefix-only matching.

💡 Suggested fix
 func isBaseTableID(ref string) bool {
-	return strings.HasPrefix(strings.TrimSpace(ref), "tbl")
+	ref = strings.TrimSpace(ref)
+	if ref == "" {
+		return false
+	}
+	// Keep ID detection strict enough to avoid misclassifying human-readable table names.
+	return strings.HasPrefix(ref, "tbl_") && !strings.ContainsAny(ref, " \t/")
 }
🤖 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 `@shortcuts/base/field_ops.go` around lines 244 - 246, isBaseTableID currently
returns true for any ref that simply starts with "tbl", causing names like "tbl
Orders" to be misclassified; update isBaseTableID to perform a stricter shape
check (for example match a well-defined ID regex such as ^tbl_[A-Za-z0-9-]+$ or
^tbl:[A-Za-z0-9]+$) and return false for anything that doesn't match so the
normal resolution path is used as a fallback; ensure the function name is kept
(isBaseTableID) so callers continue to use it and test with example inputs like
"tbl123", "tbl_abc-1", and "tbl Orders" to verify behavior.

Comment on lines +190 to +201
func listEveryTable(runtime *common.RuntimeContext, baseToken string) ([]map[string]interface{}, error) {
const pageLimit = 100
offset := 0
items := []map[string]interface{}{}
for {
batch, total, err := listAllTables(runtime, baseToken, offset, pageLimit)
if err != nil {
return nil, err
}
items = append(items, batch...)
if len(batch) == 0 || len(batch) < pageLimit || (total > 0 && len(items) >= total) {
break

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 | ⚡ Quick win

Pagination can stop after the first full page when total is omitted.

listAllTables falls back total to current page size when the API omits it. With a full first page (100) and more tables available, the len(items) >= total condition can break early and miss tables for name-based resolution in +field-list-batch.

💡 Suggested fix
-		if len(batch) == 0 || len(batch) < pageLimit || (total > 0 && len(items) >= total) {
+		if len(batch) == 0 || len(batch) < pageLimit {
 			break
 		}
🤖 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 `@shortcuts/base/table_ops.go` around lines 190 - 201, The pagination break
logic in listEveryTable is incorrectly using the returned total even when
listAllTables falls back to the current page size, causing early termination;
update the condition so total is only treated as authoritative when it exceeds
the current batch size — i.e., change the break clause to use (total >
len(batch) && len(items) >= total) instead of (total > 0 && len(items) >=
total), leaving the other checks (len(batch) == 0 || len(batch) < pageLimit)
unchanged; this ensures listEveryTable and its use of listAllTables correctly
page through all tables for name-based resolution.

Comment on lines +5 to +11
```json
{
"table_name": "任务表",
"trigger_control_list": [],
"condition": null
}
```

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 | ⚡ Quick win

Fix JSON example: field should be condition_list, not condition.

The JSON example uses "condition": null (line 9), but the field table (line 17) documents the correct field name as condition_list. This mismatch will cause confusion when users try to construct actual workflow JSON payloads.

🔧 Proposed fix
 {
   "table_name": "任务表",
   "trigger_control_list": [],
-  "condition": null
+  "condition_list": []
 }
📝 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
```json
{
"table_name": "任务表",
"trigger_control_list": [],
"condition": null
}
```
🤖 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-base/references/workflow-steps/trigger-change-record.md` around
lines 5 - 11, The JSON example uses the wrong field name "condition" — update
the example in trigger-change-record.md so the payload uses "condition_list"
(e.g., "condition_list": null or an appropriate array) instead of "condition"
and ensure the example matches the field table; verify the JSON block containing
"table_name", "trigger_control_list" and the condition field is corrected to use
"condition_list".

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

Labels

domain/base PR touches the base domain domain/im PR touches the im domain domain/mail PR touches the mail domain domain/task PR touches the task domain size/XL Architecture-level or global-impact change

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants