Files
xiongyuanwen-byted c6c040c2c5 refactor(sheets)!: remove legacy sheets command surface (#2572)
* refactor(sheets)!: remove legacy sheets command surface

The `shortcuts/sheets/backward` package kept 42 pre-refactor command names
(`+create`, `+read`, `+write`, `+create-sheet`, `+media-upload`, ...) alive
alongside the refactored ones. Monitoring puts their combined share below 5%,
so they are dropped along with the machinery that carried them.

Removed with the package:
- the `sheetsAliasReplacement` map and `wrapSheetsBackwardDeprecation`, which
  tagged each alias with a `_notice` deprecation envelope on execution;
- the deprecated cobra group and the custom `sheets --help` usage template that
  existed only to hide it. `applySheetsCompatGroups` becomes
  `applySheetsCommandGroups`: it still groups the `+`-shortcuts so the OpenAPI
  metaapi subcommands keep filing under cobra's stock "Additional Commands".

The deleted package owned no shared logic. Its `parent_type` mapping for image
uploads was, by its own header, a deliberate mirror of the canonical one in
`shortcuts/sheets/helpers.go`; `common.IsLocalOfficeToken` and the drive upload
helpers are untouched and keep their other callers.

E2E tests still drove the removed commands and are ported to the refactored
surface: `+workbook-create` / `+workbook-info` / `+cells-set` / `+cells-get` /
`+cells-search` / `+sheet-*`. The sub-sheet dry-run assertions had to be
rewritten rather than renamed, because the old commands posted to
`sheets/v2/sheets_batch_update` while the new ones invoke
`modify_workbook_structure` over `sheet_ai/v2`. `+update-sheet` fanned out to
`+sheet-rename` + `+sheet-hide` + `+dim-freeze`. Every migrated command was
verified against a live workbook, which is where the assertions come from:
rename and hide answer with a bare revision counter, so their effect is read
back from `+workbook-info` (`sheet_name`, `is_hidden`).

Also updated, since these referenced the removed surface:
- three `skills/lark-drive` reference docs that instructed agents to run
  `sheets +read` / `sheets +find`; these ship embedded in the binary, so the
  instructions would have produced unknown-subcommand errors;
- `skill-template/domains/sheets.md`, deleted: every sheets command it named
  was removed and the cell payload shape it taught
  (`{"type":"formula","text":...}`) is rejected by `+cells-set`;
- stale comments naming `backward.uploadSheetMediaFile` and
  `backward/helpers.go`.

`Shortcut.OnInvoke` and `internal/deprecation` now have no producers. Both are
generic framework plumbing wired into the `_notice` envelope in `cmd/root.go`,
so they are left in place; the `OnInvoke` doc comment no longer claims a caller.

BREAKING CHANGE: removes the 42 pre-refactor sheets commands (`+create`,
`+read`, `+write`, `+append`, `+find`, `+set-style`, `+create-sheet`,
`+update-sheet`, `+add-dimension`, `+set-dropdown`, `+media-upload`,
`+create-filter-view`, ...). They now fail with `unknown subcommand` and carry
no deprecation notice, so a caller still on the old names gets no migration
pointer at runtime.

Replacements for all 42, plus the differences that are not simple renames — the
cell payload vocabulary (`{"type":"formula","text":...}` is now rejected),
response field paths, and `+update-sheet` / `+update-dimension` fanning out to
several commands — are documented in
skills/lark-sheets/references/lark-sheets-legacy-command-migration.md, reachable
at runtime via:

    lark-cli skills read lark-sheets references/lark-sheets-legacy-command-migration.md

* test(sheets): close the assertion gaps found in review

- The append subtest asserted only the ok envelope, so a +cells-set that
  reported success without persisting would pass; the later +cells-search
  covers row 2 only. Read A4:C4 back and assert the row landed. Verified
  non-vacuous against a live sheet: an unwritten row returns cells carrying
  no value, so the read-back fails if the write does not persist.
- Cover the omitted-title +sheet-copy path. The comment on the empty-title
  suite states an omitted --title means "let the server name the copy", but
  nothing exercised it; the new case pins that new_name is absent from the
  payload rather than sent empty.
- Assert tool_name in the shared dry-run loop instead of only in the create
  case, so copy / delete / rename / move cannot pass by selecting a different
  tool on the same /tools/invoke_write endpoint.

* docs(sheets): fix the migration guide examples found in review

- `+table-put --sheets` requires the `{"sheets":[…]}` envelope; the `+append`
  row showed a bare array, which the flag rejects outright ("top level must be
  the object {\"sheets\":[…]}, got a bare JSON array"). Verified both forms
  against a dry-run before and after.
- Tag the diagnostic fence as `text` (markdownlint MD040).
2026-09-01 19:47:18 +08:00

10 KiB

Sheets CLI E2E Coverage

Metrics

  • Denominator: 30 leaf commands
  • Covered: 23
  • Coverage: 76.7%
  • Scope note: this table tracks the commands these E2E tests touch plus the gaps carried over from the pre-refactor surface. It is not a census of the whole sheets shortcut surface, which is far larger; re-auditing it against the current command list is a separate follow-up.

Summary

  • TestSheets_CRUDE2EWorkflow: proves +workbook-create, +workbook-info, +cells-set, +cells-get, +cells-search, and +workbook-export; key t.Run(...) proof points are create spreadsheet with +workbook-create as bot, read data with +cells-get as bot, find cells with +cells-search as bot, and export spreadsheet with +workbook-export as bot. +cells-search asserts both halves — one match at A2 for a term on the sheet, zero for one that is not — so a search that silently matched everything would still fail.
  • TestSheets_CreateWorkflowAsUser: proves the UAT path for sheets +workbook-create and sheets +workbook-info through create spreadsheet with +workbook-create as user and get spreadsheet info with +workbook-info as user.
  • TestSheets_SpreadsheetsResource: proves direct spreadsheets create, spreadsheets get, and spreadsheets patch.
  • TestSheets_FilterWorkflow: proves spreadsheet.sheet.filters create, get, update, and delete, with supporting sheet setup through +workbook-create, +workbook-info, and +cells-set.
  • TestSheets_SheetShortcutsDryRun: proves request shapes for +sheet-create, +sheet-copy, +sheet-delete, +sheet-rename, and +sheet-move without hitting live APIs — all five reach the backend through modify_workbook_structure, so the pinned distinction is the operation and the fields packed beside it.
  • TestSheets_SheetShortcutsWorkflow: proves live +sheet-create, +sheet-copy, +sheet-rename, +sheet-hide, +dim-freeze, and +sheet-delete flows against a real spreadsheet, verified by reading the workbook back through +workbook-info rather than by matching a field in each command's own response. Rename and hide answer with nothing but a revision counter, so the listing is the only place their effect can be observed (sheet_name, is_hidden); +dim-freeze reports frozen_rows / frozen_columns in its own result.
  • TestSheets_ImageUploadDryRunParentType: dry-run coverage for the drive parent_type an image upload carries, across every surface a local file enters through — +cells-set-image and +float-image-create. A native spreadsheet must upload as sheet_image and one backed by an imported office file as office_sheet_file; a /wiki/ ref must stay native, because a preview cannot resolve the node and so must not read the split out of the node_token. The negative half matters most: the backend does not validate parent_node against parent_type, so a wrong value uploads successfully and only surfaces later as an image that will not render.
  • TestSheets_ImageUploadDryRunChunked: dry-run coverage for the 20 MB branch in +cells-set-image. Under the ceiling the preview shows one upload_all; one byte past it, the upload_prepare / upload_part / upload_finish trio Execute actually sends. Before that dispatch existed, an oversized image failed on upload_all with a bare 1061002 naming neither the size nor the limit.
  • Cleanup note: workflow-created spreadsheets are cleaned up via drive +delete --type sheet; those cleanup-only executions are not counted as command coverage because no testcase asserts delete behavior as the primary proof surface.

Command Table

Status Cmd Type Testcase Key parameter shapes Notes / uncovered reason
✓ sheets +cells-get shortcut sheets_crud_workflow_test.go::TestSheets_CRUDE2EWorkflow/read data with +cells-get as bot --spreadsheet-token; --sheet-id; --range values are collected out of the decoded payload, not matched against a fixed path: get_cell_ranges' response nesting is the backend's
✕ sheets +cells-merge shortcut none no merge workflow yet
✕ sheets +cells-replace shortcut none no replace workflow yet
✓ sheets +cells-search shortcut sheets_crud_workflow_test.go::TestSheets_CRUDE2EWorkflow/find cells with +cells-search as bot --spreadsheet-token; --sheet-id; --find; --range asserts the hit (total_matches 1, address A2) and the miss (total_matches 0)
✓ sheets +cells-set shortcut sheets_crud_workflow_test.go::TestSheets_CRUDE2EWorkflow/write data with +cells-set as bot; sheets_crud_workflow_test.go::TestSheets_CRUDE2EWorkflow/append a row with +cells-set as bot; sheets_filter_workflow_test.go::TestSheets_FilterWorkflow/write test data for filtering as bot --spreadsheet-token; --sheet-id; --range; --cells the append subtest writes past the existing block, which is the auto-expand path the pre-refactor +append relied on
✕ sheets +cells-set-image shortcut dry-run only live image workflow still missing; parent_type shape covered by sheets_image_upload_dryrun_test.go
✓ sheets +cells-set-style shortcut sheets_call_compat_workflow_test.go::TestSheets_CallCompatWorkflow/style it with an openpyxl border weight --spreadsheet-token; --range; --border-styles
✕ sheets +cells-unmerge shortcut none no merge workflow yet
✓ sheets +dim-delete shortcut sheets_dim_workflow_test.go::TestSheets_DimShortcutsWorkflow --spreadsheet-token; --sheet-id; --ranges
✓ sheets +dim-freeze shortcut sheets_sheet_shortcuts_workflow_test.go::TestSheets_SheetShortcutsWorkflow/freeze rows and columns with +dim-freeze as bot --spreadsheet-token; --sheet-id; --rows; --cols asserts the reported frozen_rows / frozen_columns
✓ sheets +dim-insert shortcut sheets_dim_workflow_test.go::TestSheets_DimShortcutsWorkflow --spreadsheet-token; --sheet-id; --position; --count dry-run inherit-style shape also covered by sheets_sheet_shortcuts_dryrun_test.go
✕ sheets +dim-move shortcut none no dimension move workflow yet
✓ sheets +sheet-copy shortcut sheets_sheet_shortcuts_workflow_test.go::TestSheets_SheetShortcutsWorkflow/copy sheet with +sheet-copy as bot --spreadsheet-token; --sheet-id; optional --title; optional --index dry-run shape also covered by sheets_sheet_shortcuts_dryrun_test.go
✓ sheets +sheet-create shortcut sheets_sheet_shortcuts_workflow_test.go::TestSheets_SheetShortcutsWorkflow/create sheet with +sheet-create as bot; sheets_sheet_list_workflow_test.go::TestSheets_SheetListWorkflow --spreadsheet-token; --title; optional --index dry-run shape also covered by sheets_sheet_shortcuts_dryrun_test.go
✓ sheets +sheet-delete shortcut sheets_sheet_shortcuts_workflow_test.go::TestSheets_SheetShortcutsWorkflow/delete sheet with +sheet-delete as bot --spreadsheet-token; --sheet-id; --yes dry-run shape also covered by sheets_sheet_shortcuts_dryrun_test.go
✓ sheets +sheet-hide shortcut sheets_sheet_shortcuts_workflow_test.go::TestSheets_SheetShortcutsWorkflow/hide sheet with +sheet-hide as bot --spreadsheet-token; --sheet-id the command answers with a revision only, so is_hidden is read back from +workbook-info
✓ sheets +sheet-list shortcut sheets_sheet_list_workflow_test.go::TestSheets_SheetListWorkflow --spreadsheet-token pinned to forward +workbook-info's sheets entries verbatim
✕ sheets +sheet-move shortcut dry-run only no live reordering workflow yet; request shape covered by sheets_sheet_shortcuts_dryrun_test.go
✓ sheets +sheet-rename shortcut sheets_sheet_shortcuts_workflow_test.go::TestSheets_SheetShortcutsWorkflow/rename sheet with +sheet-rename as bot --spreadsheet-token; --sheet-id; --title dry-run shape also covered by sheets_sheet_shortcuts_dryrun_test.go
✓ sheets +workbook-create shortcut sheets_crud_workflow_test.go::TestSheets_CRUDE2EWorkflow/create spreadsheet with +workbook-create as bot; sheets_filter_workflow_test.go::TestSheets_FilterWorkflow/create spreadsheet with initial data as bot; sheets_create_workflow_test.go::TestSheets_CreateWorkflowAsUser/create spreadsheet with +workbook-create as user --title; --folder-token token read from data.spreadsheet.spreadsheet_token
✓ sheets +workbook-export shortcut sheets_crud_workflow_test.go::TestSheets_CRUDE2EWorkflow/export spreadsheet with +workbook-export as bot --spreadsheet-token; --file-extension; --output-path
✓ sheets +workbook-info shortcut sheets_crud_workflow_test.go::TestSheets_CRUDE2EWorkflow/get spreadsheet info with +workbook-info as bot; sheets_filter_workflow_test.go::TestSheets_FilterWorkflow/get sheet info as bot; sheets_create_workflow_test.go::TestSheets_CreateWorkflowAsUser/get spreadsheet info with +workbook-info as user --spreadsheet-token returns the workbook structure only: sub-sheets under data.sheets, no spreadsheet token echo
✓ sheets spreadsheet.sheet.filters create api sheets_filter_workflow_test.go::TestSheets_FilterWorkflow/create filter with spreadsheet.sheet.filters create as bot spreadsheet_token; sheet_id in --params; filter JSON in --data
✓ sheets spreadsheet.sheet.filters delete api sheets_filter_workflow_test.go::TestSheets_FilterWorkflow/delete filter with spreadsheet.sheet.filters delete as bot spreadsheet_token; sheet_id in --params
✓ sheets spreadsheet.sheet.filters get api sheets_filter_workflow_test.go::TestSheets_FilterWorkflow/get filter with spreadsheet.sheet.filters get as bot spreadsheet_token; sheet_id in --params
✓ sheets spreadsheet.sheet.filters update api sheets_filter_workflow_test.go::TestSheets_FilterWorkflow/update filter with spreadsheet.sheet.filters update as bot spreadsheet_token; sheet_id in --params; filter JSON in --data
✕ sheets spreadsheet.sheets find api none no direct API workflow yet
✓ sheets spreadsheets create api sheets_crud_workflow_test.go::TestSheets_SpreadsheetsResource/create spreadsheet with spreadsheets create as bot title in --data
✓ sheets spreadsheets get api sheets_crud_workflow_test.go::TestSheets_SpreadsheetsResource/get spreadsheet with spreadsheets get as bot spreadsheet_token in --params
✓ sheets spreadsheets patch api sheets_crud_workflow_test.go::TestSheets_SpreadsheetsResource/patch spreadsheet with spreadsheets patch as bot spreadsheet_token in --params; title patch in --data