Skip to content

feat(schema): output MCP tool spec envelope for all API commands#1048

Open
sang-neo03 wants to merge 23 commits into
mainfrom
feat/ai-friendly-schema
Open

feat(schema): output MCP tool spec envelope for all API commands#1048
sang-neo03 wants to merge 23 commits into
mainfrom
feat/ai-friendly-schema

Conversation

@sang-neo03
Copy link
Copy Markdown
Collaborator

@sang-neo03 sang-neo03 commented May 22, 2026

Summary

Replace lark-cli schema <path> default JSON output with MCP Tool spec compliant envelopes covering all 193 embedded API methods, so AI / MCP consumers can read the full call contract (parameters, types, conditional flags, scopes, risk, access tokens, doc URL) directly from one envelope without writing per-command Skill markdown. This is a BREAKING CHANGE to the JSON shape only; --format pretty keeps its legacy ANSI rendering and remains the developer-facing browsing view.

Changes

  • New internal/schema/ package — stateless transformation from meta_data.json to MCP envelope: Envelope / InputSchema / OutputSchema / Property (with order-preserving OrderedProps) / Meta / Affordance types in types.go; AssembleEnvelope / AssembleService / AssembleAll / ParsePath entry points in assembler.go and path.go; L1–L3 hard-fail lint + L4 coverage in lint.go; embed slot annotations/ for future affordance overlays (PR-1 stub, no YAML).
  • Envelope assembly uses embedded meta_data.json only, bypassing remote_meta.json overlay — guarantees deterministic output across machines / brands. New registry.EmbeddedSpec / EmbeddedServiceNames / EmbeddedMetaJSON helpers in internal/registry/loader.go.
  • cmd/schema/schema.go JSON branch rewired to envelope output; pretty branch (printServices / printResourceList / printMethodDetail) preserved verbatim and still walks merged registry data for runtime inspection. Cobra arg cap raised from 1 to 8; completeSchemaPath extended to handle both dotted (im.messages.reply) and space-separated (im messages reply) forms.
  • Envelope name field reflects the actual CLI argv form: nested resources keep their dotted segment (e.g. "im chat.members bots"), so name.split(" ") is directly executable as argv.
  • inputSchema.properties.yes injected for high-risk-write methods (52 envelopes); file fields encoded as type: "string" + format: "binary" + x-in: "body"; path/query parameters tagged x-in: "path"|"query"; _meta.access_tokens translated tenant→bot and sorted; _meta.required_scopes always emitted ([] when empty); _meta.doc_url and _meta.affordance omitempty.
  • 4-layer lint with documented known-list in spec §5.3 for upstream meta_data.json data-quality patterns (dangerrisk inconsistency, typeless arrays, non-standard type: "list", min==max); 20 hand-picked golden fixtures in internal/schema/testdata/golden/ covering edge cases (file upload, high-risk-write, missing risk, requiredScopes, nested response, bot-only / user-only access tokens, deep nesting, multi-segment resource paths). UPDATE_GOLDEN=1 go test ./internal/schema/... refreshes goldens after meta_data updates.
  • 7 new tests in cmd/schema/schema_test.go (envelope JSON shape, dotted-vs-spaced byte equality, bare/service list arrays, yes injection presence/absence, pretty mode preservation); TestSchemaCmd_NoArgs split into _Pretty and _JSON_IsArray for the new default output.

Test Plan

  • make unit-test passed
  • go test $(go list ./... | grep -Ev '/(tests_e2e|tests/cli_e2e)(/|$)') -short -count=1 passed
  • validate (build / vet / unit / integration / convention / security) passed
  • local-eval E2E tests_e2e/schema/... 19/19 subtests passed (8 test functions); other historical E2E packages report env-level failures unrelated to this PR (test app cli_aa9a9dde09781cb0 lacks several scopes in the Lark developer console)
  • local-eval skillave N/A (no shortcut / skill / meta API changes)
  • acceptance-reviewer passed (6/6 scenarios; 2 issues found during review and fixed in this PR: nested resource name segmentation, spec §5.3 lint known-list disclosure)
  • golangci-lint --new-from-rev=origin/main 0 issues; go mod tidy no diff
  • manual verification:
    • lark-cli schema im.images.create --format json | jq '{name, has_yes: (.inputSchema.properties.yes != null), risk: ._meta.risk}'{"name":"im images create","has_yes":false,"risk":"write"}
    • lark-cli schema im messages delete --format json | jq '._meta.risk, .inputSchema.properties.yes.type'"high-risk-write" / "boolean"
    • lark-cli schema im.chat.members.bots --format json | jq -r '.name'im chat.members bots (matches lark-cli im chat.members bots argv)
    • lark-cli schema --format pretty | head — legacy ANSI service catalog preserved

Related Issues

N/A

Summary by CodeRabbit

  • New Features

    • Schema command accepts dotted or space-separated paths with enhanced tab-completion; JSON mode returns deterministic schema envelopes.
    • Many new/updated schema fixtures added (expanded API coverage).
  • Improvements

    • Clearer validation hints and errors for schema lookups; high-risk write operations include a yes confirmation flag.
    • Stricter schema linting and coverage measurement; stable property/key ordering in schema output.
  • Tests / Quality

    • Expanded unit and golden tests to validate assembly, ordering, determinism, and lint coverage.

Review Change Stack

sang-neo03 added 19 commits May 22, 2026 15:51
Rewrites cmd/schema/schema.go so the default --format json branch emits
MCP-spec envelopes via schema.AssembleAll/AssembleService/AssembleEnvelope.
The legacy --format pretty branch is preserved verbatim and still uses
printServices / printResourceList / printMethodDetail.

Args max raised from 1 to 8 so the path can be supplied either as a single
dotted argument (im.reactions.list) or as space-separated segments
(im reactions list); both forms route through schema.ParsePath and produce
byte-identical output.

The completeSchemaPath function is extended to drive tab-completion for
both forms: legacy dotted prefix when len(args) == 0, and per-segment
resource/method completion when args already contains earlier segments.

BREAKING CHANGE: default JSON output shape changes from the raw meta_data
structure to an MCP envelope array/object. Existing scripts parsing the
old shape must either pin --format pretty or migrate to the new envelope
fields (name, description, inputSchema, outputSchema, _meta).
Replaces TestSchemaCmd_NoArgs with two variants reflecting the new default
shape: TestSchemaCmd_NoArgs_Pretty asserts the legacy "Available services"
text appears only under --format pretty, and TestSchemaCmd_NoArgs_JSON_IsArray
asserts the default JSON output parses as an envelope array with at least 180
entries.

Adds six new tests:
- TestSchemaCmd_JSONIsEnvelope: single-method output has name / description
  / inputSchema / outputSchema / _meta keys and envelope_version "1.0".
- TestSchemaCmd_SpaceSeparatedPath_EqualsDotted: dotted and space forms
  produce identical output bytes for the same command path.
- TestSchemaCmd_ServiceListIsArray: schema <service> returns a JSON array
  whose every entry's name starts with "<service> ".
- TestSchemaCmd_HighRiskYesInjection: high-risk-write commands inject
  inputSchema.properties.yes.
- TestSchemaCmd_NoYesForReadRisk: read-risk commands do not inject yes.
- TestSchemaCmd_PrettyUnchanged_KeyTextPresent: --format pretty still
  surfaces the legacy section markers (Parameters:, Response:, Identity:,
  Scopes:, CLI:).
Nested resources whose meta_data key contains a dot (e.g. chat.members,
user_mailbox.templates) were previously split on '.' and rejoined with
spaces, producing envelope names like 'im chat members bots'. AI
consumers doing name.split(' ') and feeding the result back as argv
got 'lark-cli im chat members bots' which the CLI rejects — the actual
invocation form is 'lark-cli im chat.members bots'.

Pass the dotted resource key as a single argv segment so the envelope
name 'im chat.members bots' round-trips through name.split(' ') back
to the CLI. Mirror the same convention in the golden harness so its
single-method assembly matches the live AssembleService walk.
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 22, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 44240e4d-f538-4de1-93fd-cdfd73aabd18

📥 Commits

Reviewing files that changed from the base of the PR and between 23086ae and 54389d3.

📒 Files selected for processing (3)
  • internal/schema/assembler.go
  • internal/schema/assembler_test.go
  • internal/schema/testdata/golden/im.reactions.list.json
💤 Files with no reviewable changes (1)
  • internal/schema/testdata/golden/im.reactions.list.json

📝 Walkthrough

Walkthrough

This PR adds MCP envelope types and OrderedProps, deterministic assembly from embedded meta_data.json with recorded key orders, CLI parsing/completion supporting dotted and space-separated paths, JSON/pretty output modes (JSON produced from embedded specs), linting and coverage utilities, golden fixtures, and tests exercising assembly and linting.

Changes

Schema Envelope Assembly and Validation

Layer / File(s) Summary
Envelope Types and Ordered Serialization
internal/schema/types.go, internal/schema/types_test.go
Defines Envelope, InputSchema, OutputSchema, Property, Meta, and OrderedProps with custom Marshal/Unmarshal to preserve explicit property ordering.
Embedded Registry Metadata Access
internal/registry/loader.go
Adds EmbeddedMetaJSON(), EmbeddedSpec(), and EmbeddedServiceNames() with sync.Once-backed parsing to expose deterministic embedded metadata.
Assembler core & key-order indexing
internal/schema/assembler.go
Parses embedded meta_data.json to index per-method key order; converts meta_data fields to Property (type normalization, enum extraction/coercion, min/max parsing), builds ordered InputSchema/OutputSchema, injects yes for high-risk-write, and provides AssembleEnvelope, AssembleService, and AssembleAll with MethodFilter.
Assembler tests & golden harness
internal/schema/assembler_test.go, internal/schema/golden_test.go
Extensive unit tests for property conversion, input/output/meta construction, determinism, filtering; golden harness builds envelopes and compares/updates fixtures under internal/schema/testdata/golden/*.json.
Golden fixtures
internal/schema/testdata/golden/*.json, internal/schema/annotations/.gitkeep
Adds many golden schema examples (approval, calendar, drive, im, mail, okr, sheets, slides, task, wiki) used by the golden harness; reserves annotations directory for future affordance overlays.
Envelope Linting and Coverage
internal/schema/lint.go, internal/schema/lint_test.go
Adds three-tier linting (lintEnvelope) for structural, type, and cross-field consistency plus measureCoverage and tests including TestAllEnvelopesPass against assembled embedded envelopes.
CLI Path Normalization & Schema Command
internal/schema/path.go, internal/schema/path_test.go, cmd/schema/schema.go, cmd/schema/schema_test.go
Introduces ParsePath to normalize dotted or space-separated CLI args, expands schema command to multiple positional args (Path + ExtraArgs), rewrites completion for both input styles, and dispatches to runJSONMode or runPrettyMode with strict-mode filtering; tests cover JSON/pretty outputs, path forms, and risk-based yes injection behavior.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Suggested reviewers

  • kongenpei
  • liangshuo-1

Poem

🐰 From embedded trees the envelopes bloom,
Keys keep their order, mapped out in room,
CLI paths split, completion hums along,
Lint keeps contracts tidy, firm, and strong,
Golden fixtures sing the deterministic song.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 44.86% 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 'feat(schema): output MCP tool spec envelope for all API commands' accurately describes the main change, using clear, specific terminology that explains the key transformation from JSON to MCP-compliant envelope format across all API commands.
Description check ✅ Passed The PR description is comprehensive and well-structured, covering Summary, Changes, Test Plan with checkmarks, and Related Issues sections that align with the repository template, though it exceeds typical brevity while providing extensive implementation 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 docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/ai-friendly-schema

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 and usage tips.

@github-actions github-actions Bot added the size/XL Architecture-level or global-impact change label May 22, 2026
@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 22, 2026

🚀 PR Preview Install Guide

🧰 CLI update

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

🧩 Skill update

npx skills add larksuite/cli#feat/ai-friendly-schema -y -g

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: 8

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
cmd/schema/schema_test.go (1)

16-18: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Set isolated config dir in tests that use cmdutil.TestFactory.

Please set LARKSUITE_CLI_CONFIG_DIR to t.TempDir() in these tests (or via a shared helper) to prevent cross-test config-state coupling.

As per coding guidelines **/*_test.go: Use t.Setenv("LARKSUITE_CLI_CONFIG_DIR", t.TempDir()) to isolate config state in tests.

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

In `@cmd/schema/schema_test.go` around lines 16 - 18, TestSchemaCmd_FlagParsing
uses cmdutil.TestFactory and must isolate CLI config state; before calling
cmdutil.TestFactory in TestSchemaCmd_FlagParsing, call
t.Setenv("LARKSUITE_CLI_CONFIG_DIR", t.TempDir()) (or add a shared helper that
sets this env var) so each test gets its own config directory and avoids
cross-test coupling.
🧹 Nitpick comments (1)
internal/schema/testdata/golden/wiki.spaces.create.json (1)

4-35: 💤 Low value

Consider adding explicit "required" field for consistency.

The inputSchema omits the "required" field entirely, while all other golden fixtures include an explicit "required" array (even when empty or with just required params). For deterministic serialization and structural consistency across the 193 API method envelopes, consider always emitting "required": [] when no fields are required.

Proposed addition for consistency
 "inputSchema": {
   "type": "object",
+  "required": [],
   "properties": {
🤖 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/schema/testdata/golden/wiki.spaces.create.json` around lines 4 - 35,
The inputSchema for this golden fixture is missing an explicit "required" array
which other fixtures include; update the inputSchema object (the one containing
"properties" with keys name, description, open_sharing, yes) to include a
"required" field—use "required": [] if no properties are mandatory (or list the
required property names if some must be required) so serialization is
deterministic and consistent across fixtures.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@cmd/schema/schema.go`:
- Around line 596-607: The code currently accepts remaining[0] as the method
name and silently ignores remaining[1:], causing invalid paths to be accepted;
update the handler that reads remaining and methodName (and the analogous block
that resolves operations) to explicitly reject extra trailing segments: if
len(remaining) != 1 (or if len(remaining) > 1) return
output.ErrWithHint(output.ExitValidation, "validation", fmt.Sprintf("Invalid
path: extra trailing path segments on %s.%s", serviceName, resName),
fmt.Sprintf("Got: %v", remaining[1:])), so callers of methods,
resource["methods"], methodName and the other resolution block fail fast instead
of silently resolving to a different method.

In `@internal/registry/loader.go`:
- Around line 28-30: The EmbeddedMetaJSON function (and the other embedded
getter functions referenced around lines 75-77) currently returns the
package-level byte slice directly, allowing callers to mutate shared state;
change each getter (e.g., EmbeddedMetaJSON) to return a defensive copy of the
underlying slice by allocating a new []byte and copying the contents (for
example via append([]byte(nil), embeddedMetaJSON...) or make + copy) so callers
cannot modify the original package-level slice.

In `@internal/schema/assembler_test.go`:
- Around line 549-550: Before calling registry.LoadFromMeta in the tests that
call AssembleService (e.g., the test cases invoking registry.LoadFromMeta("im")
and AssembleService("im", spec, nil)), isolate registry-backed state by setting
the config dir to a temp directory using t.Setenv("LARKSUITE_CLI_CONFIG_DIR",
t.TempDir()) at the start of each test; apply this change to the occurrences
around the registry.LoadFromMeta calls (the blocks that start with spec :=
registry.LoadFromMeta("im") / envs := AssembleService(...)) including the other
noted occurrences in the file so host config/cache cannot leak into test
results.

In `@internal/schema/lint.go`:
- Around line 153-155: The current check only recurses into array item schemas
when p.Items.Properties exists, so primitive or invalid item schemas
(p.Items.type, etc.) are not linted; update the logic in validatePropertyTypes
to always validate the p.Items schema node when p.Items != nil — if
p.Items.Properties is present recurse with
validatePropertyTypes(p.Items.Properties, false, errs), otherwise run the
schema-level validation on p.Items (the same validations used for non-array
property schemas) and append errors to errs so invalid primitive item schemas
are caught.

In `@internal/schema/testdata/golden/calendar.calendars.list.json`:
- Around line 7-14: The "page_size" schema defines type "integer" but sets
"default" as the string "500"; update the default to an integer (500) to match
the declared type for the page_size property in the JSON schema (look for the
"page_size" object and its "default" field) so the default value's type aligns
with "type": "integer".

In
`@internal/schema/testdata/golden/mail.user_mailbox.template.attachments.download_url.json`:
- Around line 24-28: The "attachment_ids" schema entry uses an invalid JSON
Schema type "list"; update the "attachment_ids" object so its "type" is "array"
(replace "list" with "array") and, if missing, add an "items" definition (e.g.,
{"type":"string"} or the appropriate item schema) to match other golden files
and restore JSON Schema validation for the property.

In `@internal/schema/testdata/golden/mail.user_mailbox.templates.create.json`:
- Around line 291-292: The create operation currently has "danger": true but an
incorrect "risk": "read"; update the risk value for the create with scope
"mail:user_mailbox.message:modify" (and any other create entries marked
danger:true) to an appropriate write-level risk such as "write" or
"high-risk-write" so it matches the modify/create semantics and danger flag;
locate the JSON entry containing "danger": true and "risk": "read" associated
with the mail:user_mailbox.message:modify create operation and change "risk" to
"write" (or "high-risk-write" if applicable).

In `@internal/schema/types.go`:
- Around line 85-88: The MarshalJSON method on OrderedProps currently returns
"{}" when o.Order is empty, which drops any entries present in o.Map; change
MarshalJSON (for receiver OrderedProps) so that when o is nil return "{}", but
if o.Order is empty and o.Map is non-nil/non-empty it marshals the entries from
o.Map (instead of returning "{}"), otherwise preserve existing behavior that
emits fields in o.Order; reference OrderedProps.MarshalJSON, the Order slice,
and the Map field when implementing the fix.

---

Outside diff comments:
In `@cmd/schema/schema_test.go`:
- Around line 16-18: TestSchemaCmd_FlagParsing uses cmdutil.TestFactory and must
isolate CLI config state; before calling cmdutil.TestFactory in
TestSchemaCmd_FlagParsing, call t.Setenv("LARKSUITE_CLI_CONFIG_DIR",
t.TempDir()) (or add a shared helper that sets this env var) so each test gets
its own config directory and avoids cross-test coupling.

---

Nitpick comments:
In `@internal/schema/testdata/golden/wiki.spaces.create.json`:
- Around line 4-35: The inputSchema for this golden fixture is missing an
explicit "required" array which other fixtures include; update the inputSchema
object (the one containing "properties" with keys name, description,
open_sharing, yes) to include a "required" field—use "required": [] if no
properties are mandatory (or list the required property names if some must be
required) so serialization is deterministic and consistent across fixtures.
🪄 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: 68123346-b43c-48f9-831e-92fc4c6cee2c

📥 Commits

Reviewing files that changed from the base of the PR and between 4582dfd and 762dc64.

📒 Files selected for processing (33)
  • cmd/schema/schema.go
  • cmd/schema/schema_test.go
  • internal/registry/loader.go
  • internal/schema/annotations/.gitkeep
  • internal/schema/assembler.go
  • internal/schema/assembler_test.go
  • internal/schema/golden_test.go
  • internal/schema/lint.go
  • internal/schema/lint_test.go
  • internal/schema/path.go
  • internal/schema/path_test.go
  • internal/schema/testdata/golden/approval.instances.cancel.json
  • internal/schema/testdata/golden/approval.instances.get.json
  • internal/schema/testdata/golden/calendar.calendars.list.json
  • internal/schema/testdata/golden/calendar.events.create.json
  • internal/schema/testdata/golden/drive.files.copy.json
  • internal/schema/testdata/golden/im.chats.create.json
  • internal/schema/testdata/golden/im.images.create.json
  • internal/schema/testdata/golden/im.messages.delete.json
  • internal/schema/testdata/golden/im.pins.delete.json
  • internal/schema/testdata/golden/im.reactions.create.json
  • internal/schema/testdata/golden/im.reactions.list.json
  • internal/schema/testdata/golden/mail.user_mailbox.folders.delete.json
  • internal/schema/testdata/golden/mail.user_mailbox.messages.get.json
  • internal/schema/testdata/golden/mail.user_mailbox.template.attachments.download_url.json
  • internal/schema/testdata/golden/mail.user_mailbox.templates.create.json
  • internal/schema/testdata/golden/okr.objectives.delete.json
  • internal/schema/testdata/golden/sheets.spreadsheet.sheet.filters.create.json
  • internal/schema/testdata/golden/slides.xml_presentation.slide.replace.json
  • internal/schema/testdata/golden/task.tasks.delete.json
  • internal/schema/testdata/golden/wiki.spaces.create.json
  • internal/schema/types.go
  • internal/schema/types_test.go

Comment thread cmd/schema/schema.go
Comment on lines +28 to +30
func EmbeddedMetaJSON() []byte {
return embeddedMetaJSON
}
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

Return defensive copies from embedded getters.

These functions currently expose mutable package-level slices. A caller can mutate shared state and affect subsequent assembly determinism.

🔧 Proposed fix
 func EmbeddedMetaJSON() []byte {
-	return embeddedMetaJSON
+	if len(embeddedMetaJSON) == 0 {
+		return nil
+	}
+	out := make([]byte, len(embeddedMetaJSON))
+	copy(out, embeddedMetaJSON)
+	return out
 }
@@
 func EmbeddedServiceNames() []string {
 	parseEmbeddedServices()
-	return embeddedServiceNames
+	if len(embeddedServiceNames) == 0 {
+		return nil
+	}
+	out := make([]string, len(embeddedServiceNames))
+	copy(out, embeddedServiceNames)
+	return out
 }

Also applies to: 75-77

🤖 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/registry/loader.go` around lines 28 - 30, The EmbeddedMetaJSON
function (and the other embedded getter functions referenced around lines 75-77)
currently returns the package-level byte slice directly, allowing callers to
mutate shared state; change each getter (e.g., EmbeddedMetaJSON) to return a
defensive copy of the underlying slice by allocating a new []byte and copying
the contents (for example via append([]byte(nil), embeddedMetaJSON...) or make +
copy) so callers cannot modify the original package-level slice.

Comment thread internal/schema/assembler_test.go
Comment thread internal/schema/lint.go Outdated
Comment thread internal/schema/testdata/golden/calendar.calendars.list.json
Comment thread internal/schema/testdata/golden/mail.user_mailbox.templates.create.json Outdated
Comment thread internal/schema/types.go
- coerce enum literals to typed JSON values (integer to int64,
  number to float64, boolean to bool) so type:"integer" fields no
  longer emit string enums; sort numeric/boolean enums while
  preserving meta_data order for string enums that carry semantic
  priority
- translate non-standard meta_data type:"list" to JSON Schema
  type:"array" with items:{} fallback when element shape is absent
  (covers the two mail attachment_ids fields)
- render inputSchema.required even when empty so consumers see a
  stable envelope shape ("[]" means no required fields, not "field
  is missing")
- reject trailing path segments in both JSON and pretty modes so
  schema im.messages.delete.foo errors instead of silently
  returning the delete method
- drop dead "list type" entry from lint_test isKnownDataInconsistency
  whitelist now that list values are translated upstream
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: 1

♻️ Duplicate comments (2)
internal/schema/testdata/golden/mail.user_mailbox.templates.create.json (1)

291-292: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Use write-level risk for this dangerous create operation.

danger: true with modify scope on a create endpoint should not be classified as "read"; this under-classifies operation safety.

🔧 Suggested fix
   "danger": true,
-  "risk": "read",
+  "risk": "write",
🤖 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/schema/testdata/golden/mail.user_mailbox.templates.create.json`
around lines 291 - 292, The create template currently marks a dangerous create
operation with "danger": true but incorrectly sets "risk": "read"; update the
JSON in mail.user_mailbox.templates.create (keys "danger" and "risk") to use a
write-level risk (change "risk" from "read" to "write") so the create endpoint
is classified correctly as a modifying operation.
internal/schema/testdata/golden/calendar.calendars.list.json (1)

8-12: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Match default type with declared integer schema type.

page_size is declared as "type": "integer" but uses a string default "500".

🔧 Suggested fix
       "page_size": {
         "type": "integer",
-        "default": "500",
+        "default": 500,
🤖 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/schema/testdata/golden/calendar.calendars.list.json` around lines 8
- 12, The JSON schema property "page_size" declares "type": "integer" but its
"default" is the string "500"; change the default value for the page_size
property to an integer (500) so the default type matches the declared schema
type (look for the page_size property in the calendar.calendars.list JSON schema
and replace the quoted "500" with the numeric 500).
🤖 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/schema/assembler.go`:
- Around line 414-430: The code currently always calls sortEnum after building
p.Enum from optsRaw, which alphabetizes string enums; change the logic in the
branch that builds p.Enum (using optsRaw, om, raw, coerceEnumValue) to preserve
the source order for string enums by only invoking sortEnum(p.Type, p.Enum) when
p.Type is numeric or boolean (i.e., when p.Type != "string"), leaving p.Enum in
the appended order for string types; ensure you still dedupe using seen and keep
using coerceEnumValue and p.Enum append as before, and update
TestConvertProperty_OptionsToEnum expectations to no longer assume alphabetical
ordering for string enums.

---

Duplicate comments:
In `@internal/schema/testdata/golden/calendar.calendars.list.json`:
- Around line 8-12: The JSON schema property "page_size" declares "type":
"integer" but its "default" is the string "500"; change the default value for
the page_size property to an integer (500) so the default type matches the
declared schema type (look for the page_size property in the
calendar.calendars.list JSON schema and replace the quoted "500" with the
numeric 500).

In `@internal/schema/testdata/golden/mail.user_mailbox.templates.create.json`:
- Around line 291-292: The create template currently marks a dangerous create
operation with "danger": true but incorrectly sets "risk": "read"; update the
JSON in mail.user_mailbox.templates.create (keys "danger" and "risk") to use a
write-level risk (change "risk" from "read" to "write") so the create endpoint
is classified correctly as a modifying operation.
🪄 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: ad7072a4-90d0-410b-8edb-16a74e2d39f1

📥 Commits

Reviewing files that changed from the base of the PR and between 762dc64 and e7076bf.

📒 Files selected for processing (11)
  • cmd/schema/schema.go
  • internal/schema/assembler.go
  • internal/schema/assembler_test.go
  • internal/schema/lint_test.go
  • internal/schema/testdata/golden/calendar.calendars.list.json
  • internal/schema/testdata/golden/im.chats.create.json
  • internal/schema/testdata/golden/mail.user_mailbox.messages.get.json
  • internal/schema/testdata/golden/mail.user_mailbox.template.attachments.download_url.json
  • internal/schema/testdata/golden/mail.user_mailbox.templates.create.json
  • internal/schema/testdata/golden/wiki.spaces.create.json
  • internal/schema/types.go
💤 Files with no reviewable changes (1)
  • internal/schema/lint_test.go

Comment thread internal/schema/assembler.go Outdated
CI fix
- Replace hard-coded absolute key-order assertions in TestKeyOrderIndex_*
  and TestBuildInputSchema_* with set-membership and propagation invariants;
  the upstream meta_data API does not guarantee stable JSON key order across
  fetches, so the old tests were flaky on CI by design.
- Skip byte-level TestGoldenEnvelopes when CI=true; golden snapshots are a
  manual refresh artefact tied to a specific meta_data fetch, not a CI gate.
- Add TestMain to isolate registry-backed tests from any host ~/.lark-cli
  cache (LARKSUITE_CLI_CONFIG_DIR + LARKSUITE_CLI_REMOTE_META=off) so the
  suite gives the same answer on every machine.

CodeRabbit review actionables
- EmbeddedServiceNames returns a defensive copy so callers cannot mutate
  the package-level slice and affect subsequent assembly determinism.
- coerceEnumValue is now also applied to default literals: integer fields
  no longer ship default: "500" — they ship default: 500 (same idea as the
  earlier enum coercion fix).
- options-branch string enums preserve meta_data source order, matching the
  enum-branch policy; only numeric/boolean enums get sorted.
- validatePropertyTypes now validates the array element schema itself
  (type, nested items), not only items.properties — previously a primitive
  element with an invalid type (e.g. items.type="list") slipped past lint.
- OrderedProps.MarshalJSON falls back to alphabetical key order when Map
  has entries but Order is empty, instead of silently emitting {}.

Tests pass locally and with CI=true env (simulating GitHub Actions).
Re-generated with UPDATE_GOLDEN=1 against the current meta_data.json
snapshot. The bulk of the diff is upstream noise (description wording,
enum entries, field order) which the CI snapshot diff can no longer
reasonably gate (see previous commit). Side-effects of the code fixes
in the parent commit are also captured:

  - integer-typed defaults now emit numeric literals (e.g. page_size
    default 500, not "500") thanks to coerceEnumValue
  - mail.user_mailbox.templates.create _meta.risk corrects to "write"
    (assembler already emitted "write"; the old golden was stale)
@codecov
Copy link
Copy Markdown

codecov Bot commented May 23, 2026

Codecov Report

❌ Patch coverage is 65.39409% with 281 lines in your changes missing coverage. Please review.
✅ Project coverage is 67.85%. Comparing base (fbe4cc6) to head (54389d3).
⚠️ Report is 6 commits behind head on main.

Files with missing lines Patch % Lines
cmd/schema/schema.go 28.42% 132 Missing and 9 partials ⚠️
internal/schema/assembler.go 83.05% 46 Missing and 25 partials ⚠️
internal/registry/loader.go 0.00% 28 Missing ⚠️
internal/schema/types.go 57.14% 12 Missing and 9 partials ⚠️
internal/schema/lint.go 81.65% 12 Missing and 8 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1048      +/-   ##
==========================================
+ Coverage   67.76%   67.85%   +0.09%     
==========================================
  Files         590      594       +4     
  Lines       55188    55937     +749     
==========================================
+ Hits        37398    37956     +558     
- Misses      14679    14819     +140     
- Partials     3111     3162      +51     

☔ View full report in Codecov by Sentry.
📢 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.

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
internal/schema/testdata/golden/im.reactions.list.json (1)

30-34: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

page_size.default type mismatches declared schema type.

page_size is declared as integer, but default is "" (string). This makes the contract internally inconsistent.

Suggested fix
       "page_size": {
         "type": "integer",
         "description": "分页大小,用于限制一次请求返回的数据条目数。;;**默认值**:20",
-        "default": "",
+        "default": 20,
         "example": "10",
         "maximum": 50,
         "x-in": "query"
       },
🤖 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/schema/testdata/golden/im.reactions.list.json` around lines 30 - 34,
The schema for the "page_size" property has a default value of "" (string) but
its type is integer; update the "page_size" property's "default" to an integer
(e.g., 20 to match the description) so the "page_size" entry's "type",
"default", and description remain consistent; locate the "page_size" object in
the JSON and replace the empty-string default with a numeric value.
🤖 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/schema/assembler_test.go`:
- Around line 20-27: TestMain currently defers cleanup which never runs because
os.Exit skips defers and also silently continues on MkdirTemp error; change it
to fail-fast on MkdirTemp error (return non-zero exit immediately) and ensure
cleanup always runs by storing the result of m.Run in a variable, calling
os.RemoveAll(dir) after the run, then calling os.Exit(code). Only call
os.Setenv("LARKSUITE_CLI_CONFIG_DIR", dir) and the other env setup after
MkdirTemp succeeds; reference the TestMain function, MkdirTemp, os.Setenv,
os.RemoveAll, m.Run, and os.Exit when making the change.

---

Outside diff comments:
In `@internal/schema/testdata/golden/im.reactions.list.json`:
- Around line 30-34: The schema for the "page_size" property has a default value
of "" (string) but its type is integer; update the "page_size" property's
"default" to an integer (e.g., 20 to match the description) so the "page_size"
entry's "type", "default", and description remain consistent; locate the
"page_size" object in the JSON and replace the empty-string default with a
numeric value.
🪄 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: 284a855d-b800-42a3-b879-acf198593e58

📥 Commits

Reviewing files that changed from the base of the PR and between e7076bf and 23086ae.

📒 Files selected for processing (19)
  • internal/registry/loader.go
  • internal/schema/assembler.go
  • internal/schema/assembler_test.go
  • internal/schema/golden_test.go
  • internal/schema/lint.go
  • internal/schema/testdata/golden/approval.instances.get.json
  • internal/schema/testdata/golden/calendar.calendars.list.json
  • internal/schema/testdata/golden/calendar.events.create.json
  • internal/schema/testdata/golden/drive.files.copy.json
  • internal/schema/testdata/golden/im.chats.create.json
  • internal/schema/testdata/golden/im.reactions.create.json
  • internal/schema/testdata/golden/im.reactions.list.json
  • internal/schema/testdata/golden/mail.user_mailbox.folders.delete.json
  • internal/schema/testdata/golden/mail.user_mailbox.messages.get.json
  • internal/schema/testdata/golden/mail.user_mailbox.templates.create.json
  • internal/schema/testdata/golden/sheets.spreadsheet.sheet.filters.create.json
  • internal/schema/testdata/golden/slides.xml_presentation.slide.replace.json
  • internal/schema/testdata/golden/wiki.spaces.create.json
  • internal/schema/types.go
✅ Files skipped from review due to trivial changes (4)
  • internal/schema/testdata/golden/im.reactions.create.json
  • internal/schema/testdata/golden/approval.instances.get.json
  • internal/schema/testdata/golden/sheets.spreadsheet.sheet.filters.create.json
  • internal/schema/testdata/golden/im.chats.create.json

Comment thread internal/schema/assembler_test.go Outdated
- TestMain: cleanup now runs reliably. os.Exit skips deferred functions,
  so the previous defer os.RemoveAll(dir) never executed. Replace defer
  with explicit cleanup, and fail fast if MkdirTemp errors instead of
  silently running against the host cache (which defeats isolation).
- convertProperty default coercion: when the literal cannot be coerced to
  the declared type (e.g. default:"" on integer field, used by meta_data
  to mean "no default"), omit the field entirely rather than emit a
  type-mismatched default. Removes a contract violation flagged on
  im.reactions.list.json#page_size.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature size/XL Architecture-level or global-impact change

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant