Skip to content

feat(doc): warn when replace_range targets a heading but markdown lacks matching heading#619

Open
herbertliu wants to merge 2 commits intomainfrom
feat/docs-update-heading-warning
Open

feat(doc): warn when replace_range targets a heading but markdown lacks matching heading#619
herbertliu wants to merge 2 commits intomainfrom
feat/docs-update-heading-warning

Conversation

@herbertliu
Copy link
Copy Markdown
Collaborator

@herbertliu herbertliu commented Apr 23, 2026

Summary

Addresses Case 2 in the lark-cli pitfall list: replace_range cannot change a block's type, so using it to "demote" a heading block to a paragraph silently fails — the API returns success but the heading styling persists.

This PR extends the existing docsUpdateWarnings infrastructure (from the PR #569 pre-write semantic check series) with a targeted, high-signal warning: when the invocation is --mode=replace_range --selection-by-title "## X" and --markdown does not lead with a matching heading level, the user almost certainly expects a block-type change that won't happen.

Changes

  • shortcuts/doc/docs_update_check.go:
    • New checkDocsUpdateReplaceHeadingBlockType — fires only on replace_range + heading-style --selection-by-title + non-matching first-prose-line heading level
    • New narrow helpers atxHeadingLevel (ATX-only, CommonMark-conformant) and firstProseHeadingLevel (reuses existing fence-tracking helpers)
    • Signature of docsUpdateWarnings extended with selectionByTitle argument
  • shortcuts/doc/docs_update.go: pass --selection-by-title through to docsUpdateWarnings
  • shortcuts/doc/docs_update_check_test.go: table-driven tests (8 cases on the check + 9 on the ATX helper); 100% coverage on the new function

Scope notes

The warning is deliberately narrow:

  • Fires only when --selection-by-title is used. Other selection modes (--selection-with-ellipsis, bare token) can target any block and would produce false positives.
  • Fires only when the markdown's first non-blank, non-fenced prose line is not an ATX heading of the same level.
  • Non-blocking — the update still proceeds, consistent with the existing warnings from feat(doc): add pre-write semantic warnings to docs +update #569.

Test Plan

  • go test ./shortcuts/doc/... passes
  • go vet ./... clean
  • gofmt -l clean
  • New coverage on checkDocsUpdateReplaceHeadingBlockType is 100%

Summary by CodeRabbit

  • Bug Fixes

    • Added a validation warning when using replace mode with a title-based selection: alerts if the selected heading style/level conflicts with the replacement and recommends delete + insert instead.
  • Tests

    • Expanded test coverage for heading detection and warning generation, including ATX heading edge cases and skipped fenced-code scenarios.

…ks a matching heading

Lark's block model stores {type, content} as independent attributes, so
replace_range rewrites content only and will not downgrade a heading
block to a paragraph. When --selection-by-title selects a heading but
--markdown leads with plain prose (or a different heading level), the
user's intent to change block type is silently dropped and the block
keeps its heading styling.

Extend docsUpdateWarnings with checkDocsUpdateReplaceHeadingBlockType
that fires only on the high-signal combination of replace_range +
heading selection-by-title + non-matching markdown first prose line.
Other selection modes can target arbitrary blocks and would produce
too many false positives, so they are left untouched.

The helper atxHeadingLevel is intentionally narrow (ATX only, requires
a trailing space per CommonMark, caps at level 6) and firstProseHeadingLevel
reuses the existing fence-tracking helpers so fenced code samples are
skipped consistently with the other warnings.
@github-actions github-actions Bot added domain/ccm PR touches the ccm domain size/M Single-domain feat or fix with limited business impact labels Apr 23, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 23, 2026

📝 Walkthrough

Walkthrough

Adds heading-level validation to docs update warnings: when mode == replace_range and --selection-by-title targets an ATX heading, the warning generator now checks the replacement markdown's first ATX heading level and emits a warning if levels differ.

Changes

Cohort / File(s) Summary
Core Warning Integration
shortcuts/doc/docs_update.go, shortcuts/doc/docs_update_check.go
Threads --selection-by-title into docsUpdateWarnings and implements a new check that, for replace_range, compares ATX heading level from the selection vs. the replacement markdown and emits a warning when levels mismatch (skips fenced code blocks; ignores non-ATX/setext cases).
Tests
shortcuts/doc/docs_update_check_test.go
Updates calls to the new function signature and adds tests for checkDocsUpdateReplaceHeadingBlockType and atxHeadingLevel covering valid ATX levels, mismatches, fenced-code skipping, and various invalid/edge-case inputs.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested reviewers

  • fangshuyu-768

Poem

🐰 I hop through docs with careful sight,
Counting # and ## to get levels right.
If replace_range tries to shift the crown,
I'll tap your code and gently frown.
Safe headings kept — a rabbit's delight ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% 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(doc): warn when replace_range targets a heading but markdown lacks matching heading' clearly and specifically describes the main change: adding a warning for a specific pitfall when using replace_range mode with heading selection but mismatched markdown.
Description check ✅ Passed The description includes all required sections from the template: Summary explains the motivation and scope clearly, Changes lists the main modifications across three files with detailed explanations, Test Plan checks all verification steps, and Related Issues acknowledges none are directly linked.
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/docs-update-heading-warning

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.

@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 23, 2026

Codecov Report

❌ Patch coverage is 91.66667% with 5 lines in your changes missing coverage. Please review.
✅ Project coverage is 60.10%. Comparing base (bc6590a) to head (13829fa).

Files with missing lines Patch % Lines
shortcuts/doc/docs_update_check.go 93.22% 3 Missing and 1 partial ⚠️
shortcuts/doc/docs_update.go 0.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #619      +/-   ##
==========================================
+ Coverage   60.05%   60.10%   +0.04%     
==========================================
  Files         406      406              
  Lines       43089    43147      +58     
==========================================
+ Hits        25877    25933      +56     
- Misses      15194    15195       +1     
- Partials     2018     2019       +1     

☔ 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

🧹 Nitpick comments (1)
shortcuts/doc/docs_update_check.go (1)

23-47: Update the warning inventory comment.

docsUpdateWarnings now emits a third warning, but the “Warnings emitted” comment still documents only the multiline and bold+italic checks.

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

In `@shortcuts/doc/docs_update_check.go` around lines 23 - 47, The top-of-file
"Warnings emitted" comment is out of date: docsUpdateWarnings now can emit a
third warning from checkDocsUpdateReplaceHeadingBlockType but the comment only
lists the multiline and bold+italic warnings; update that inventory to include a
third entry describing the replace-heading/block-type warning (mentioning
replace_range/replace_all behavior when headings change block type or
selectionByTitle interactions) so the comment matches the current behavior of
docsUpdateWarnings and the three helper functions
checkDocsUpdateReplaceMultilineMarkdown, checkDocsUpdateBoldItalic, and
checkDocsUpdateReplaceHeadingBlockType.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@shortcuts/doc/docs_update_check.go`:
- Around line 86-101: The atxHeadingLevel function currently trims all leading
whitespace and misclassifies headings; update it to call fenceIndentOK on the
original line to enforce CommonMark 0–3 leading spaces rule, then scan the run
of '#' to compute level, allow either a space or a tab (or nothing for empty
heading) after the hashes (i.e., accept "##\tH2" and "##" as valid), and return
the computed level for empty headings instead of 0; reference the
atxHeadingLevel function and the fenceIndentOK helper and add tests for "    ##
code": 0, "\t## code": 0, "##\tH2": 2 and "##": 2.

---

Nitpick comments:
In `@shortcuts/doc/docs_update_check.go`:
- Around line 23-47: The top-of-file "Warnings emitted" comment is out of date:
docsUpdateWarnings now can emit a third warning from
checkDocsUpdateReplaceHeadingBlockType but the comment only lists the multiline
and bold+italic warnings; update that inventory to include a third entry
describing the replace-heading/block-type warning (mentioning
replace_range/replace_all behavior when headings change block type or
selectionByTitle interactions) so the comment matches the current behavior of
docsUpdateWarnings and the three helper functions
checkDocsUpdateReplaceMultilineMarkdown, checkDocsUpdateBoldItalic, and
checkDocsUpdateReplaceHeadingBlockType.
🪄 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: 7bba7985-e5ff-45a9-97b8-bd6c00b31160

📥 Commits

Reviewing files that changed from the base of the PR and between bc6590a and 5cf3db9.

📒 Files selected for processing (3)
  • shortcuts/doc/docs_update.go
  • shortcuts/doc/docs_update_check.go
  • shortcuts/doc/docs_update_check_test.go

Comment thread shortcuts/doc/docs_update_check.go Outdated
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 23, 2026

🚀 PR Preview Install Guide

🧰 CLI update

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

🧩 Skill update

npx skills add larksuite/cli#feat/docs-update-heading-warning -y -g

CodeRabbit flagged that the first-cut atxHeadingLevel used strings.TrimSpace,
which silently accepted 4+ leading spaces and leading tabs (CommonMark treats
both as indented code blocks, not headings) and rejected two valid forms:
empty headings (e.g. '##') and hash-tab-text ('##\tTitle').

Rewrite using the existing fenceIndentOK helper so leading-space handling
stays consistent with the fence-open/close logic, accept space or tab as the
closing delimiter of the opening hash run, and treat a bare '#' run with no
trailing content as a valid empty heading.

Callers that accept whitespace-tolerant user input (the selection-by-title
flag) now trim explicitly before calling the helper; the markdown-payload
caller keeps raw lines so leading-space semantics stay CommonMark-accurate.

Also fix a golangci-lint copyloopvar warning in the new table-driven test
(redundant 'tc := tc' on Go 1.22+).
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)
shortcuts/doc/docs_update_check.go (1)

17-37: ⚠️ Potential issue | 🟡 Minor

Update the warning inventory comment for the new third check.

The comment still says “Both checks” and lists only two warnings, but Line 45 now adds the heading block-type warning.

Suggested doc-comment update
-// Both checks ignore fenced code blocks (```…``` and ~~~…~~~, with up
-// to 3 leading spaces per CommonMark §4.5), inline code spans, and
-// backslash-escaped emphasis markers so that literal Markdown content
-// embedded in code samples or escaped prose does not produce false
-// positives.
+// The checks avoid fenced code blocks where relevant (```…``` and
+// ~~~…~~~, with up to 3 leading spaces per CommonMark §4.5). The
+// bold+italic check also ignores inline code spans and backslash-escaped
+// emphasis markers so literal Markdown samples or escaped prose do not
+// produce false positives.
@@
 //  2. Lark does not round-trip bold+italic. Six shapes are detected:
 //     ***text***   ___text___
 //     **_text_**   __*text*__
 //     _**text**_   *__text__*
 //     Lark stores only one of the two emphases (usually italic), silently
 //     dropping the other. The user wanted both; they will get one.
+//
+//  3. replace_range with --selection-by-title targeting a heading does not
+//     change block type. If --markdown does not lead with a matching ATX
+//     heading level, the target keeps its existing heading styling.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@shortcuts/doc/docs_update_check.go` around lines 17 - 37, The file comment
for docsUpdateWarnings is out of date: it still says “Both checks” and lists
only two warnings while a third ("replace_range with --selection-by-title
targeting a heading does not change block type") was added; update the
doc-comment above func docsUpdateWarnings to describe three checks, reword the
fenced-code/inline-code coverage as suggested (mention fenced blocks and that
the bold+italic check ignores inline code spans and backslash-escaped emphasis),
and append the new third warning about replace_range/--selection-by-title
leaving heading block type unchanged so the comment matches the implementation.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@shortcuts/doc/docs_update_check.go`:
- Around line 96-115: The atxHeadingLevel function fails on CRLF-terminated
empty ATX headings because body can end with '\r' (e.g., "##\r"); update
atxHeadingLevel (and its use of fenceIndentOK/body inspection) to either trim a
trailing '\r' from body before analysis or treat '\r' as whitespace in the final
switch (i.e., consider '\r' like ' ' and '\t'), and add a regression test that
feeds a CRLF payload whose first prose line is an empty heading like "##\r\n" to
ensure firstProseHeadingLevel/atxHeadingLevel returns 2 instead of 0.

---

Outside diff comments:
In `@shortcuts/doc/docs_update_check.go`:
- Around line 17-37: The file comment for docsUpdateWarnings is out of date: it
still says “Both checks” and lists only two warnings while a third
("replace_range with --selection-by-title targeting a heading does not change
block type") was added; update the doc-comment above func docsUpdateWarnings to
describe three checks, reword the fenced-code/inline-code coverage as suggested
(mention fenced blocks and that the bold+italic check ignores inline code spans
and backslash-escaped emphasis), and append the new third warning about
replace_range/--selection-by-title leaving heading block type unchanged so the
comment matches the implementation.
🪄 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: f63604df-7c39-42f2-953f-fa6e6814e380

📥 Commits

Reviewing files that changed from the base of the PR and between 5cf3db9 and 13829fa.

📒 Files selected for processing (2)
  • shortcuts/doc/docs_update_check.go
  • shortcuts/doc/docs_update_check_test.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • shortcuts/doc/docs_update_check_test.go

Comment thread shortcuts/doc/docs_update_check.go
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

domain/ccm PR touches the ccm domain size/M Single-domain feat or fix with limited business impact

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant