Skip to content

feat: add framework-level output projection with --full - #1537

Closed
luozhixiong01 wants to merge 1 commit into
mainfrom
feat/output-projection-shortcut
Closed

feat: add framework-level output projection with --full#1537
luozhixiong01 wants to merge 1 commit into
mainfrom
feat/output-projection-shortcut

Conversation

@luozhixiong01

@luozhixiong01 luozhixiong01 commented Jun 22, 2026

Copy link
Copy Markdown
Collaborator

Summary

Adds a framework-level (domain-agnostic) output-projection capability to the shortcut runtime: a Projectable shortcut declares an OutputSchema, the runtime trims the default view to the curated whitelist, and a boolean --full escape hatch restores the complete upstream payload. The default view is a positive whitelist — fields not declared (including future upstream additions) are full-only. im read shortcuts are the first adopters (verbose fields like chat avatar URLs, tenant_key, and message sender internals waste agent context); other domains' read shortcuts are unchanged, since projection is opt-in per shortcut. Measured: +chat-list default is ~46% smaller than --full.

Changes

  • Add projection engine shortcuts/common/projection.go (ProjectBySchema normalizes to canonical JSON, then keeps only Projected fields; KeepFields/ArrayOf/ObjectOf builders) + unit tests projection_test.go
  • Wire projection into the runner: shortcuts/common/runner.go (emit/outFormat apply projection, register --full, jq-miss stderr hint), types.go (Projectable/OutputSchema/NoFullViewHint), internal/schema/types.go (Projected tag), internal/output/jq.go (jq filter counts)
  • Declare OutputSchema on 5 im read shortcuts: +chat-list, +chat-search, +flag-list, +feed-shortcut-list, +feed-group-list
  • Tighten message sender to {id, sender_type, name} and decouple name resolution in shortcuts/im/convert_lib/{content_convert,helpers}.go; add a no-full redirect hint on 4 message commands
  • Update 9 skills/lark-im/references/*.md (curated-view / --full --jq usage) and add a curated-view/--full convention to skills/lark-shared/SKILL.md
  • Scope: projection is opt-in via OutputSchema; only the 5 im read shortcuts above adopt it in this PR — all other domains' read output is unchanged

Test Plan

  • make unit-test passed
  • validate passed (build / vet / unit / integration)
  • local-eval (sandbox): E2E 10/10 passed; skillave 6/7 — the 1 fail is an out-of-scope generic-delivery case (all projection decision points pass), accepted at human gate
  • acceptance-reviewer passed (8/8 cases)
  • manual verification: lark-cli im +chat-list --as user --page-size 20 --format json returns 6554 B (curated) vs 12146 B with --full (46% smaller default); --full --jq '.data.chats[].avatar' retrieves the hidden field

Related Issues

N/A

Summary by CodeRabbit

  • New Features
    • Added schema-curated default output for chat/list/search and feed/flag commands; use --full to show the complete payload.
    • When --jq targets fields hidden from the default view, the CLI now suggests rerunning with --full (or --full --jq <path>).
  • Behavior Changes
    • Message sender output is now minimized to only id and optional sender_type.
  • Documentation
    • Updated command reference docs with “Default view and --full” and minimal sender / no-full-view guidance.
  • Tests
    • Added coverage for projection and output-schema helpers.

@github-actions github-actions Bot added domain/im PR touches the im domain size/L Large or sensitive change across domains or core paths labels Jun 22, 2026
@coderabbitai

coderabbitai Bot commented Jun 22, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 90986c91-c210-427d-b2a9-744a65ac35b0

📥 Commits

Reviewing files that changed from the base of the PR and between bbb23f3 and c3708c2.

📒 Files selected for processing (17)
  • internal/output/jq.go
  • internal/schema/types.go
  • shortcuts/common/projection.go
  • shortcuts/common/projection_test.go
  • shortcuts/common/runner.go
  • shortcuts/common/types.go
  • shortcuts/im/convert_lib/content_convert.go
  • shortcuts/im/convert_lib/helpers.go
  • shortcuts/im/im_chat_list.go
  • shortcuts/im/im_chat_messages_list.go
  • shortcuts/im/im_chat_search.go
  • shortcuts/im/im_feed_group_list.go
  • shortcuts/im/im_feed_shortcut_list.go
  • shortcuts/im/im_flag_list.go
  • shortcuts/im/im_messages_mget.go
  • shortcuts/im/im_messages_search.go
  • shortcuts/im/im_threads_messages_list.go
💤 Files with no reviewable changes (15)
  • shortcuts/im/im_threads_messages_list.go
  • shortcuts/im/im_messages_mget.go
  • shortcuts/im/im_chat_messages_list.go
  • shortcuts/im/convert_lib/content_convert.go
  • shortcuts/im/im_feed_group_list.go
  • shortcuts/im/im_chat_list.go
  • shortcuts/im/im_chat_search.go
  • shortcuts/im/convert_lib/helpers.go
  • shortcuts/common/types.go
  • shortcuts/im/im_feed_shortcut_list.go
  • shortcuts/im/im_flag_list.go
  • shortcuts/im/im_messages_search.go
  • shortcuts/common/projection_test.go
  • shortcuts/common/projection.go
  • shortcuts/common/runner.go
✅ Files skipped from review due to trivial changes (1)
  • internal/schema/types.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • internal/output/jq.go

📝 Walkthrough

Walkthrough

Adds schema-driven default output projection for shortcut responses, with --full support and count-aware --jq handling. IM shortcuts now declare projected output schemas or no-full-view hints, sender output is narrowed, and the IM reference docs describe the new default-view behavior.

Changes

Output Projection Framework

Layer / File(s) Summary
Schema and Shortcut type contracts
internal/schema/types.go, shortcuts/common/types.go
schema.Property gains Projected; Shortcut gains Projectable, OutputSchema, and NoFullViewHint.
Projection engine and schema builders
shortcuts/common/projection.go, shortcuts/common/projection_test.go
Adds ProjectBySchema, recursive projection helpers, schema builders, and tests covering projection, dropped fields, and builder behavior.
jq count helpers
internal/output/jq.go
jqFilter returns (count, error); JqFilterCount and JqFilterRawCount expose counts to callers.
Runner projection wiring and --full flag
shortcuts/common/runner.go
Projection is applied in JSON and non-JSON output paths, --jq can warn on full-only fields, and --full is conditionally registered for projectable shortcuts.
IM sender narrowing
shortcuts/im/convert_lib/content_convert.go, shortcuts/im/convert_lib/helpers.go
sender output is reduced to a minimal map shape and sender-name lookup uses a new unresolved-ID helper.
IM shortcut schema declarations
shortcuts/im/im_chat_list.go, shortcuts/im/im_chat_search.go, shortcuts/im/im_feed_group_list.go, shortcuts/im/im_feed_shortcut_list.go, shortcuts/im/im_flag_list.go, shortcuts/im/im_chat_messages_list.go, shortcuts/im/im_messages_mget.go, shortcuts/im/im_messages_search.go, shortcuts/im/im_threads_messages_list.go
IM shortcuts now define projected output schemas or NoFullViewHint values for the curated default view behavior.
IM reference documentation
skills/lark-im/references/lark-im-chat-list.md, skills/lark-im/references/lark-im-chat-search.md, skills/lark-im/references/lark-im-feed-group-list.md, skills/lark-im/references/lark-im-feed-shortcut-list.md, skills/lark-im/references/lark-im-flag-list.md, skills/lark-im/references/lark-im-chat-messages-list.md, skills/lark-im/references/lark-im-messages-mget.md, skills/lark-im/references/lark-im-messages-search.md, skills/lark-im/references/lark-im-threads-messages-list.md, skills/lark-shared/SKILL.md
Adds default-view and --full guidance across the IM reference docs and shared skill guide.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • larksuite/cli#638: Shares the same internal/output/jq.go jq filtering path, including the JqFilter/JqFilterRaw flow.
  • larksuite/cli#820: Touches IM list output fields and metadata that overlap with the curated output shapes added here.

Suggested labels

domain/base

Suggested reviewers

  • YangJunzhou-01
  • SunPeiYang996

🐇 Curly hops and tidy rows,
Fields now show just what the schema knows.
With --full for hidden things to gleam,
And --jq riding the bunny stream.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 67.35% 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 summarizes the new framework-level output projection and --full escape hatch.
Description check ✅ Passed The description follows the template and includes summary, changes, test plan, and related issues sections with concrete 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.
✨ 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 feat/output-projection-shortcut

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.

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

🧹 Nitpick comments (2)
shortcuts/common/projection_test.go (1)

178-200: 🧹 Nitpick | 🔵 Trivial | ⚡ Quick win

Add an array-item nested case to TestAnyProjected.

Current coverage validates nested object traversal but not Items.Properties. Please add one assertion for a schema where only array-element fields are marked projected, so guard behavior is locked for both object and array trees.

Suggested test addition
 func TestAnyProjected(t *testing.T) {
 	none := &schema.OrderedProps{}
 	none.Set("a", schema.Property{})
 	if anyProjected(none) {
 		t.Fatalf("no marks should report false")
 	}
@@
 	if anyProjected(nil) {
 		t.Fatalf("nil should report false")
 	}
+
+	arrElem := &schema.OrderedProps{}
+	arrElem.Set("id", schema.Property{Projected: true})
+	arr := &schema.OrderedProps{}
+	arr.Set("items", schema.Property{
+		Type:  "array",
+		Items: &schema.Property{Type: "object", Properties: arrElem},
+	})
+	if !anyProjected(arr) {
+		t.Fatalf("array item nested mark should report true")
+	}
 }

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/common/projection_test.go` around lines 178 - 200, The
TestAnyProjected function needs an additional test case to verify that the
anyProjected function correctly handles nested array items. Add a new assertion
within the TestAnyProjected function that creates a schema.OrderedProps with a
property of type "array", sets Items with Properties containing a
schema.Property marked with Projected: true, then verifies that anyProjected
returns true for this nested array case. This ensures guard behavior is tested
for both object and array tree traversal, not just object nesting.

Source: Coding guidelines

shortcuts/im/convert_lib/helpers.go (1)

212-213: 🧹 Nitpick | 🔵 Trivial | ⚡ Quick win

Skip already-attempted IDs by key presence, not non-empty value.

Line 212 re-queues IDs when nameMap[id] == "", so previously attempted-but-unresolved users can be queried repeatedly across ResolveSenderNames calls.

Proposed patch
 func unresolvedUserSenderIDs(messages []map[string]interface{}, nameMap map[string]string) []string {
 	var ids []string
 	seen := map[string]bool{}
 	for _, msg := range messages {
 		s, _ := msg["sender"].(map[string]interface{})
 		id, _ := s["id"].(string)
-		if id == "" || !strings.HasPrefix(id, "ou_") || seen[id] || nameMap[id] != "" {
+		if id == "" || !strings.HasPrefix(id, "ou_") || seen[id] {
+			continue
+		}
+		if _, attempted := nameMap[id]; attempted {
 			continue
 		}
 		seen[id] = true
 		ids = append(ids, id)
 	}
 	return ids
 }
🤖 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/im/convert_lib/helpers.go` around lines 212 - 213, The condition on
line 212 currently checks nameMap[id] != "" to skip IDs that have been
processed, but this causes previously attempted-but-unresolved users to be
queried repeatedly. Instead of checking for a non-empty value in nameMap, use
Go's comma-ok idiom to check if the key exists in the map. Replace the
nameMap[id] != "" check with a proper key existence check using the pattern _,
exists := nameMap[id], then only skip the ID if the key is present in the map,
regardless of whether the value is empty or not.
🤖 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 `@shortcuts/common/projection.go`:
- Around line 132-149: The anyProjected function fails to check for projected
marks nested under array item properties, only examining p.Properties during
recursion. After the recursive call to anyProjected for p.Properties in the
function body, add an additional check that also recursively calls anyProjected
on p.Items.Properties if p.Items is not nil. This ensures that projected fields
within array elements are properly detected and prevents schemas that project
only array element fields from being incorrectly treated as pass-through.

---

Nitpick comments:
In `@shortcuts/common/projection_test.go`:
- Around line 178-200: The TestAnyProjected function needs an additional test
case to verify that the anyProjected function correctly handles nested array
items. Add a new assertion within the TestAnyProjected function that creates a
schema.OrderedProps with a property of type "array", sets Items with Properties
containing a schema.Property marked with Projected: true, then verifies that
anyProjected returns true for this nested array case. This ensures guard
behavior is tested for both object and array tree traversal, not just object
nesting.

In `@shortcuts/im/convert_lib/helpers.go`:
- Around line 212-213: The condition on line 212 currently checks nameMap[id] !=
"" to skip IDs that have been processed, but this causes previously
attempted-but-unresolved users to be queried repeatedly. Instead of checking for
a non-empty value in nameMap, use Go's comma-ok idiom to check if the key exists
in the map. Replace the nameMap[id] != "" check with a proper key existence
check using the pattern _, exists := nameMap[id], then only skip the ID if the
key is present in the map, regardless of whether the value is empty or not.
🪄 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: 79cf9a17-1e58-4e84-b997-8391f898bb1a

📥 Commits

Reviewing files that changed from the base of the PR and between 824aa9e and bbb23f3.

📒 Files selected for processing (27)
  • internal/output/jq.go
  • internal/schema/types.go
  • shortcuts/common/projection.go
  • shortcuts/common/projection_test.go
  • shortcuts/common/runner.go
  • shortcuts/common/types.go
  • shortcuts/im/convert_lib/content_convert.go
  • shortcuts/im/convert_lib/helpers.go
  • shortcuts/im/im_chat_list.go
  • shortcuts/im/im_chat_messages_list.go
  • shortcuts/im/im_chat_search.go
  • shortcuts/im/im_feed_group_list.go
  • shortcuts/im/im_feed_shortcut_list.go
  • shortcuts/im/im_flag_list.go
  • shortcuts/im/im_messages_mget.go
  • shortcuts/im/im_messages_search.go
  • shortcuts/im/im_threads_messages_list.go
  • skills/lark-im/references/lark-im-chat-list.md
  • skills/lark-im/references/lark-im-chat-messages-list.md
  • skills/lark-im/references/lark-im-chat-search.md
  • skills/lark-im/references/lark-im-feed-group-list.md
  • skills/lark-im/references/lark-im-feed-shortcut-list.md
  • skills/lark-im/references/lark-im-flag-list.md
  • skills/lark-im/references/lark-im-messages-mget.md
  • skills/lark-im/references/lark-im-messages-search.md
  • skills/lark-im/references/lark-im-threads-messages-list.md
  • skills/lark-shared/SKILL.md

Comment on lines +132 to +149
// anyProjected reports whether any field in the tree carries Projected==true.
// Used as a guard: a Projectable command whose OutputSchema marks nothing is
// treated as pass-through rather than trimming everything away.
func anyProjected(props *schema.OrderedProps) bool {
if props == nil {
return false
}
for _, key := range props.Order {
p := props.Map[key]
if p.Projected {
return true
}
if anyProjected(p.Properties) {
return true
}
}
return false
}

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

anyProjected misses projected marks nested under array items.

At Line 144, recursion only checks p.Properties; it ignores p.Items.Properties. That makes anyProjected return false for schemas that project only array element fields, so projection can be incorrectly treated as pass-through.

Proposed fix
 func anyProjected(props *schema.OrderedProps) bool {
 	if props == nil {
 		return false
 	}
 	for _, key := range props.Order {
 		p := props.Map[key]
 		if p.Projected {
 			return true
 		}
-		if anyProjected(p.Properties) {
+		if anyProjected(childProps(p)) {
 			return true
 		}
 	}
 	return false
 }
🤖 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/common/projection.go` around lines 132 - 149, The anyProjected
function fails to check for projected marks nested under array item properties,
only examining p.Properties during recursion. After the recursive call to
anyProjected for p.Properties in the function body, add an additional check that
also recursively calls anyProjected on p.Items.Properties if p.Items is not nil.
This ensures that projected fields within array elements are properly detected
and prevents schemas that project only array element fields from being
incorrectly treated as pass-through.

Adds a domain-agnostic output-projection engine to the shortcut runtime; a Projectable shortcut declares an OutputSchema and the runtime trims the default view to the curated whitelist, with a boolean --full to restore the full upstream payload. The 5 im read shortcuts are the first adopters; other domains are unchanged (projection is opt-in via OutputSchema). Message senders are tightened to {id, sender_type, name}; adds a jq-miss stderr hint.
@luozhixiong01
luozhixiong01 force-pushed the feat/output-projection-shortcut branch from bbb23f3 to c3708c2 Compare June 26, 2026 09:12
@codecov

codecov Bot commented Jun 26, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 76.71958% with 44 lines in your changes missing coverage. Please review.
✅ Project coverage is 74.64%. Comparing base (39d60cb) to head (c3708c2).

Files with missing lines Patch % Lines
shortcuts/common/runner.go 13.88% 24 Missing and 7 partials ⚠️
shortcuts/common/projection.go 90.78% 5 Missing and 2 partials ⚠️
internal/output/jq.go 60.00% 6 Missing ⚠️
Additional details and impacted files
@@           Coverage Diff            @@
##             main    #1537    +/-   ##
========================================
  Coverage   74.64%   74.64%            
========================================
  Files         806      807     +1     
  Lines       81386    81550   +164     
========================================
+ Hits        60752    60875   +123     
- Misses      16101    16133    +32     
- Partials     4533     4542     +9     

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

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

@github-actions

Copy link
Copy Markdown

🚀 PR Preview Install Guide

🧰 CLI update

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

🧩 Skill update

npx skills add larksuite/cli#feat/output-projection-shortcut -y -g

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

Labels

domain/im PR touches the im domain feature size/L Large or sensitive change across domains or core paths

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant