fix(mail): hide lint counts + tip from default writepath envelope#834
fix(mail): hide lint counts + tip from default writepath envelope#834xulong3370 wants to merge 3 commits into
Conversation
Add an HTML lint library + Larksuite-native autofix to lark-cli mail, plus the skills/lark-mail/ skill bundle (2 reference docs, 5 HTML templates, the +lint-html shortcut, and writing-path lint integration across all 6 compose shortcuts). Lint library (shortcuts/mail/lint/) - Error: drop dangerous tags (<script> / <iframe> / <form> / <input> / <link> / <object> / <embed>), on* event handlers, javascript: / vbscript: / file: URLs. - Warning + autofix: rewrite HTML4-era <font> / <center> / <marquee> / <blink>. - Larksuite-native autofix: rewrite <p> / <ul> / <ol> / <li> / <blockquote> / <a> to mail-editor native markup so AI can write the simplest HTML and still produce native-quality rendering. - Inline-style and URL-scheme allow-list filtering. - <style> block passthrough (server adds CSS scope class). +lint-html shortcut (preview / CI) Read-only HTML preview tool. Default envelope returns only cleaned_html; --show-lint-details adds full warnings[] / errors[]. --strict exits non- zero on any finding (CI gate). Writing-path lint in 6 compose shortcuts +send / +draft-create / +reply / +reply-all / +forward / +draft-edit body op all run lint before drafting: - lint_applied_count / original_blocked_count: always present. - lint_applied[] / original_blocked[]: only with --show-lint-details. - compose_hint: points AI consumers to the HTML writing guide. skills/lark-mail/ skill bundle 5 pre-rendered Larksuite-native HTML templates: weekly newsletter, personal weekly report, team weekly report, market research report, résumé. 2 reference docs: - references/lark-mail-html.md: writing rules + format primitives + template-usage flow. - references/lark-mail-lint-html.md: +lint-html usage + return-value contract + 9 worked examples. SKILL.md updates linking the new docs and templates. Sealed conventions - @user mention chip: id="at-user-N" is the only hard requirement; do not write data-user-id. - Highlight palette: 3 colors (pink milestones, yellow follow-ups, green completed); black text, no bold / padding / border-radius. - Brand color palette: main black, 3 levels of grey, Lark blue / deep blue, alert red, emergency orange, light pink / light grey backgrounds, border grey. - URL scheme allow-list: http(s): / mailto: / cid: / data:image/* only. - Inline-style + tag allow-lists. - Writing-style floor: subject <= 50 chars, decision-first, lists instead of mechanical numbering, emoji only as status tags. Tests - shortcuts/mail/lint/...: unit tests for every rule. - shortcuts/mail/mail_lint_html_test.go: +lint-html envelope contract. - shortcuts/mail/mail_lint_writepath_test.go: writing-path envelope contract. - 5 templates verified via +draft-create smoke test.
…ow-lint-details The 2 count fields were unconditionally written into the writepath envelope's data map, leaking lint metadata into the default-mode envelope and violating the tech-design "same in, same out" rule for the 4 lint fields. Move them inside the existing showDetails branch so all 4 lint fields (2 counts + 2 arrays) appear together only when the caller asks for detail. Also updates applyLintToEnvelope's doc comment and the matching unit / integration tests in mail_lint_writepath_test.go to assert the new default-hidden behavior (counts now absent in default mode; present together with arrays only when --show-lint-details=true). sprint: S1
The tip text was unconditionally written into the JSON envelope returned by
buildDraftSavedOutput for +send / +reply / +reply-all / +forward save-as-draft
paths, but the tech-design only sanctions {compose_hint, draft_id, reference}
for the default envelope and never mentions tip. Drop tip from the .data map.
The same teaching string is still emitted on stderr by hintSendDraft, so user
UX is unchanged. Flip the matching test assertions to require tip absent.
sprint: S2
📝 WalkthroughWalkthroughThis PR introduces comprehensive HTML linting and sanitization for mail composition via a new ChangesMail HTML Linting System
Compose Shortcut Integration
Output Envelope Simplification
Registry and Documentation
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Poem
✨ Finishing Touches🧪 Generate unit tests (beta)
|
There was a problem hiding this comment.
Actionable comments posted: 11
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (4)
skills/lark-mail/references/lark-mail-send.md (1)
107-109:⚠️ Potential issue | 🟠 Major | ⚡ Quick winDocumentation inconsistent with PR Bug B fix.
According to the PR objectives, Bug B removes the
tipentry frombuildDraftSavedOutput's returned map (the stdout JSON envelope). However, line 108 still shows"tip"in the 草稿模式 return value example. Per the PR summary, the tip string is now emitted on stderr byhintSendDraftinstead, so the JSON envelope should only containdraft_id(and optionallyreferencewhen available, per line 113).📋 Proposed fix to align with Bug B
```json { "ok": true, "data": { - "draft_id": "草稿ID", - "tip": "draft saved. To send: lark-cli mail user_mailbox.drafts send --params '{...}'" + "draft_id": "草稿ID" } }Add a note below the example explaining that the hint message is now printed to stderr instead. </details> <details> <summary>🤖 Prompt for AI Agents</summary>Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.In
@skills/lark-mail/references/lark-mail-send.mdaround lines 107 - 109, Update
the 草稿模式 JSON example to match Bug B by removing the "tip" entry from the
buildDraftSavedOutput stdout envelope (so only "draft_id" and optionally
"reference" remain), and add a brief note below the example stating that the
hint message is now emitted to stderr by hintSendDraft; ensure you reference
buildDraftSavedOutput and hintSendDraft so reviewers can locate the related
behavior.</details> </blockquote></details> <details> <summary>skills/lark-mail/references/lark-mail-reply.md (1)</summary><blockquote> `98-100`: _⚠️ Potential issue_ | _🟠 Major_ | _⚡ Quick win_ **Documentation inconsistent with PR Bug B fix.** Line 99 still shows the `"tip"` field, but this was removed from the default envelope per PR Bug B. <details> <summary>📋 Proposed fix</summary> ```diff ```json { "ok": true, "data": { - "draft_id": "草稿ID", - "tip": "draft saved. To send: lark-cli mail user_mailbox.drafts send --params '{...}'" + "draft_id": "草稿ID" } }</details> <details> <summary>🤖 Prompt for AI Agents</summary>Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.In
@skills/lark-mail/references/lark-mail-reply.mdaround lines 98 - 100, The
documentation example still includes the removed "tip" field; update the JSON
example in lark-mail-reply.md so the response object only contains "draft_id"
under "data" (remove the "tip" entry and its message), and ensure any
surrounding explanatory text or example output references are adjusted to match
the new envelope returned by the code that only returns "draft_id".</details> </blockquote></details> <details> <summary>skills/lark-mail/references/lark-mail-reply-all.md (1)</summary><blockquote> `95-97`: _⚠️ Potential issue_ | _🟠 Major_ | _⚡ Quick win_ **Documentation inconsistent with PR Bug B fix.** Line 96 still shows the `"tip"` field in the default envelope, but PR Bug B removes this field. <details> <summary>📋 Proposed fix</summary> ```diff ```json { "ok": true, "data": { - "draft_id": "草稿ID", - "tip": "draft saved. To send: lark-cli mail user_mailbox.drafts send --params '{...}'" + "draft_id": "草稿ID" } }</details> <details> <summary>🤖 Prompt for AI Agents</summary>Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.In
@skills/lark-mail/references/lark-mail-reply-all.mdaround lines 95 - 97,
Remove the obsolete "tip" field from the default response envelope used in the
reply-all reference example: update the JSON sample that currently shows both
"draft_id" and "tip" to only include "draft_id" (i.e., delete the "tip": "draft
saved. To send: lark-cli mail user_mailbox.drafts send --params '{...}'" entry)
so the example matches the PR Bug B change.</details> </blockquote></details> <details> <summary>skills/lark-mail/references/lark-mail-forward.md (1)</summary><blockquote> `93-96`: _⚠️ Potential issue_ | _🟠 Major_ | _⚡ Quick win_ **Documentation inconsistent with PR Bug B fix.** Line 95 still shows the `"tip"` field in the 草稿模式 return value, but PR Bug B removes this field from the default envelope. The tip message is now emitted on stderr instead. <details> <summary>📋 Proposed fix</summary> ```diff ```json { "ok": true, "data": { - "draft_id": "草稿ID", - "tip": "draft saved. To send: lark-cli mail user_mailbox.drafts send --params '{...}'" + "draft_id": "草稿ID" } }</details> <details> <summary>🤖 Prompt for AI Agents</summary>Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.In
@skills/lark-mail/references/lark-mail-forward.mdaround lines 93 - 96,
Update the example JSON in skills/lark-mail/references/lark-mail-forward.md to
match PR Bug B by removing the now-removed "tip" field from the 草稿模式 return
value; locate the JSON object containing "draft_id" and remove the "tip": "draft
saved. To send: lark-cli mail user_mailbox.drafts send --params '{...}'" entry
so the returned data only includes "draft_id".</details> </blockquote></details> </blockquote></details>🧹 Nitpick comments (1)
shortcuts/mail/mail_send_confirm_output_test.go (1)
90-104: ⚡ Quick winCover the stderr handoff in the regression test.
These assertions only prove that
tipdisappeared from the JSON map. Please add one save-draft integration assertion that the send hint still lands on stderr, so the channel split itself cannot regress.As per coding guidelines
stdout is data (JSON envelopes), stderr is everything else (progress, warnings, hints). Never mix them as it corrupts pipe chains.🤖 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/mail/mail_send_confirm_output_test.go` around lines 90 - 104, The test currently only asserts that "tip" is omitted from the JSON envelope produced by buildDraftSavedOutput/DraftResult, but doesn't verify that the "send hint" is still written to stderr; update mail_send_confirm_output_test.go to capture the process stderr when exercising the save-draft path (the same invocation that calls buildDraftSavedOutput or the helper that executes the save-draft flow) and add an assertion that stderr contains the expected send hint text (e.g., the hint string used when saving drafts), while keeping the existing stdout JSON assertions to ensure the channel split (stdout for JSON envelope, stderr for hints) is preserved.🤖 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/mail/lint/linter.go`: - Around line 69-75: Finalize strict-mode promotion (Options.Strict) before calculating rep.HasErrorFindings and rep.HasWarningFindings: after walk(...) and applyFeishuNativeStyles(...) but before setting rep.HasErrorFindings/rep.HasWarningFindings, run the normalization step that promotes warning severities to error for findings (e.g., transform entries in rep.Applied and any STYLE_PROPERTY_DROPPED or Feishu-native rewrite findings) when Options.Strict is true so the subsequent flags reflect the promoted severities; ensure you update the severity fields on rep.Applied/rep.Blocked as needed and then compute rep.HasErrorFindings = len(rep.Blocked) > 0 and rep.HasWarningFindings = len(rep.Applied) > 0. - Around line 735-738: The code declares and populates a local map variable named `priority` (created with make(map[string]int, len(order)) and filled in the loop `for i, k := range order { priority[strings.ToLower(k)] = i + 1 }`) but never uses it; remove the unused `priority` declaration and the entire loop that fills it to fix the unused-local compile error, or if the map is intended to be used later, replace its usage appropriately where `priority` is referenced instead of deleting—ensure no other code expects `priority` after removal. In `@shortcuts/mail/lint/rules.go`: - Around line 201-241: The scheme-cleaning step currently removes control bytes but leaves plain spaces (U+0020), so inputs like "java script:..." are misclassified; update the rune filter in the strings.Map call (the block that assigns value by mapping runes) to also drop U+0020 (space) so the subsequent colon search and scheme extraction (scheme := strings.ToLower(value[:colon])) treat embedded spaces as removed; ensure this change still preserves removal of control bytes and DEL and keeps the rest of the logic around allowedURLSchemes, blockedURLSchemes and the "data" handling intact. In `@shortcuts/mail/mail_lint_writepath.go`: - Around line 19-23: The help text for the flag showLintDetailsFlag is misleading: update its Desc to state that lint fields are omitted by default and only included when --show-lint-details=true to match applyLintToEnvelope behavior; edit the Desc for the variable showLintDetailsFlag to clearly say that the four lint fields (lint_applied, original_blocked and their counts) are omitted unless the flag is true, referencing the applyLintToEnvelope logic that controls inclusion. In `@skills/lark-mail/assets/templates/job-application--resume.html`: - Around line 16-23: The template contains invalid nested list wrappers like repeated "<ul data-list-bullet=\"true\">...<ul data-list-bullet=\"true\">...</ul></ul>" and "<ol ... data-list-number=\"true\">...<ol ...>...</ol></ol>" which produce non-LI children; update the HTML so each list wrapper only directly contains <li> elements by either flattening the pair into a single indented <ul> or <ol> (remove the outer/inner duplicate wrapper), or move the inner list inside a real <li> (so the inner "<ul>" becomes a child of a corresponding "<li class=\"temp-li ...\">"); ensure instances with class="temp-li bullet2" and data-ol-id="work"/"proj" follow this rule and keep styling attributes on the remaining valid list elements. In `@skills/lark-mail/references/lark-mail-draft-create.md`: - Line 11: The link in the CRITICAL prerequisite currently points to "references/lark-mail-html.md" but the correct relative target is the sibling file "lark-mail-html.md"; update the markdown link text to reference "lark-mail-html.md" (replace "references/lark-mail-html.md" with "lark-mail-html.md") wherever it appears in the CRITICAL line so the prerequisite points to the correct file in the same directory. In `@skills/lark-mail/references/lark-mail-draft-edit.md`: - Line 15: The CRITICAL note currently links to "references/lark-mail-html.md" which is incorrect for this directory; update the Markdown link target in the note (the string "references/lark-mail-html.md") to a same-directory relative path such as "./lark-mail-html.md" (or "lark-mail-html.md") so the link resolves correctly from skills/lark-mail/references/lark-mail-draft-edit.md. In `@skills/lark-mail/references/lark-mail-forward.md`: - Around line 16-17: The markdown has an unmatched closing bold marker in the sentence "编辑邮件内容前 MUST 先用 Read 工具读取 [references/lark-mail-html.md](references/lark-mail-html.md),其中包含邮件书写规范**" — fix by removing the trailing "**" or by adding a matching opening "**" before the text you intend to bold (update the line containing that sentence in lark-mail-forward.md so bold markers are balanced). In `@skills/lark-mail/references/lark-mail-reply-all.md`: - Around line 16-17: The markdown line containing "编辑邮件内容前 MUST 先用 Read 工具读取 [references/lark-mail-html.md](references/lark-mail-html.md),其中包含邮件书写规范**" has an unmatched closing bold marker; fix it by either removing the trailing "**" or adding a matching opening "**" before the intended bold text so the Markdown is valid—update the sentence in lark-mail-reply-all.md accordingly to use proper bold markers. In `@skills/lark-mail/references/lark-mail-reply.md`: - Around line 20-21: The markdown line ending with an unmatched bold delimiter ("编辑邮件内容前 MUST 先用 Read 工具读取 [references/lark-mail-html.md](references/lark-mail-html.md),其中包含邮件书写规范**") has a stray closing **; fix by either removing the trailing ** or adding a matching opening ** before the intended bolded text so the bold markup is balanced (edit the sentence text fragment shown to ensure matching ** delimiters). In `@skills/lark-mail/references/lark-mail-send.md`: - Around line 15-16: The line "编辑邮件内容前 MUST 先用 Read 工具读取 [references/lark-mail-html.md](references/lark-mail-html.md),其中包含邮件书写规范**" has a stray closing bold marker (**); fix the malformed bold by either removing the trailing "**" or by adding a matching opening "**" before the text you intend to bold so the markdown becomes balanced—update the sentence in the lark-mail-send.md content accordingly. --- Outside diff comments: In `@skills/lark-mail/references/lark-mail-forward.md`: - Around line 93-96: Update the example JSON in skills/lark-mail/references/lark-mail-forward.md to match PR Bug B by removing the now-removed "tip" field from the 草稿模式 return value; locate the JSON object containing "draft_id" and remove the "tip": "draft saved. To send: lark-cli mail user_mailbox.drafts send --params '{...}'" entry so the returned data only includes "draft_id". In `@skills/lark-mail/references/lark-mail-reply-all.md`: - Around line 95-97: Remove the obsolete "tip" field from the default response envelope used in the reply-all reference example: update the JSON sample that currently shows both "draft_id" and "tip" to only include "draft_id" (i.e., delete the "tip": "draft saved. To send: lark-cli mail user_mailbox.drafts send --params '{...}'" entry) so the example matches the PR Bug B change. In `@skills/lark-mail/references/lark-mail-reply.md`: - Around line 98-100: The documentation example still includes the removed "tip" field; update the JSON example in lark-mail-reply.md so the response object only contains "draft_id" under "data" (remove the "tip" entry and its message), and ensure any surrounding explanatory text or example output references are adjusted to match the new envelope returned by the code that only returns "draft_id". In `@skills/lark-mail/references/lark-mail-send.md`: - Around line 107-109: Update the 草稿模式 JSON example to match Bug B by removing the "tip" entry from the buildDraftSavedOutput stdout envelope (so only "draft_id" and optionally "reference" remain), and add a brief note below the example stating that the hint message is now emitted to stderr by hintSendDraft; ensure you reference buildDraftSavedOutput and hintSendDraft so reviewers can locate the related behavior. --- Nitpick comments: In `@shortcuts/mail/mail_send_confirm_output_test.go`: - Around line 90-104: The test currently only asserts that "tip" is omitted from the JSON envelope produced by buildDraftSavedOutput/DraftResult, but doesn't verify that the "send hint" is still written to stderr; update mail_send_confirm_output_test.go to capture the process stderr when exercising the save-draft path (the same invocation that calls buildDraftSavedOutput or the helper that executes the save-draft flow) and add an assertion that stderr contains the expected send hint text (e.g., the hint string used when saving drafts), while keeping the existing stdout JSON assertions to ensure the channel split (stdout for JSON envelope, stderr for hints) is preserved.🪄 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:
719573fe-2cdd-4724-b6a9-28d36b93c7ac📒 Files selected for processing (33)
shortcuts/mail/helpers.goshortcuts/mail/lint/linter.goshortcuts/mail/lint/linter_test.goshortcuts/mail/lint/rules.goshortcuts/mail/lint/types.goshortcuts/mail/mail_draft_create.goshortcuts/mail/mail_draft_create_test.goshortcuts/mail/mail_draft_edit.goshortcuts/mail/mail_forward.goshortcuts/mail/mail_lint_html.goshortcuts/mail/mail_lint_html_test.goshortcuts/mail/mail_lint_writepath.goshortcuts/mail/mail_lint_writepath_test.goshortcuts/mail/mail_reply.goshortcuts/mail/mail_reply_all.goshortcuts/mail/mail_send.goshortcuts/mail/mail_send_confirm_output_test.goshortcuts/mail/shortcuts.goskill-template/domains/mail.mdskills/lark-mail/SKILL.mdskills/lark-mail/assets/templates/job-application--resume.htmlskills/lark-mail/assets/templates/newsletter--weekly-brief.htmlskills/lark-mail/assets/templates/research--market-report.htmlskills/lark-mail/assets/templates/weekly--personal-report.htmlskills/lark-mail/assets/templates/weekly--team-report.htmlskills/lark-mail/references/lark-mail-draft-create.mdskills/lark-mail/references/lark-mail-draft-edit.mdskills/lark-mail/references/lark-mail-forward.mdskills/lark-mail/references/lark-mail-html.mdskills/lark-mail/references/lark-mail-lint-html.mdskills/lark-mail/references/lark-mail-reply-all.mdskills/lark-mail/references/lark-mail-reply.mdskills/lark-mail/references/lark-mail-send.md
| walk(root, &rep, opts) | ||
| applyFeishuNativeStyles(root, &rep, opts) | ||
|
|
||
| rep.HasErrorFindings = len(rep.Blocked) > 0 | ||
| rep.HasWarningFindings = len(rep.Applied) > 0 | ||
| rep.CleanedHTML = renderFragment(root) | ||
|
|
There was a problem hiding this comment.
Finalize strict-mode promotion before computing the report flags.
Options.Strict is documented as promoting warnings to errors, but STYLE_PROPERTY_DROPPED and the Feishu-native rewrite findings can still reach this point in Applied with warning severity. That makes +lint-html --strict report different severities depending on which path produced the finding.
Suggested normalization point
walk(root, &rep, opts)
applyFeishuNativeStyles(root, &rep, opts)
+
+ if opts.Strict && len(rep.Applied) > 0 {
+ promoted := make([]Finding, len(rep.Applied))
+ copy(promoted, rep.Applied)
+ for i := range promoted {
+ promoted[i].Severity = SeverityError
+ }
+ rep.Blocked = append(rep.Blocked, promoted...)
+ rep.Applied = rep.Applied[:0]
+ }
rep.HasErrorFindings = len(rep.Blocked) > 0
rep.HasWarningFindings = len(rep.Applied) > 0
rep.CleanedHTML = renderFragment(root)🤖 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/mail/lint/linter.go` around lines 69 - 75, Finalize strict-mode
promotion (Options.Strict) before calculating rep.HasErrorFindings and
rep.HasWarningFindings: after walk(...) and applyFeishuNativeStyles(...) but
before setting rep.HasErrorFindings/rep.HasWarningFindings, run the
normalization step that promotes warning severities to error for findings (e.g.,
transform entries in rep.Applied and any STYLE_PROPERTY_DROPPED or Feishu-native
rewrite findings) when Options.Strict is true so the subsequent flags reflect
the promoted severities; ensure you update the severity fields on
rep.Applied/rep.Blocked as needed and then compute rep.HasErrorFindings =
len(rep.Blocked) > 0 and rep.HasWarningFindings = len(rep.Applied) > 0.
| priority := make(map[string]int, len(order)) | ||
| for i, k := range order { | ||
| priority[strings.ToLower(k)] = i + 1 | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
sed -n '731,762p' shortcuts/mail/lint/linter.go
python - <<'PY'
from pathlib import Path
import re
text = Path("shortcuts/mail/lint/linter.go").read_text()
m = re.search(r'func reorderAttrs\(n \*xhtml\.Node, order \[\]string\) \{(?P<body>.*?)^\}', text, re.S | re.M)
body = m.group("body")
for i, line in enumerate(body.splitlines(), 1):
if "priority" in line:
print(f"{i}: {line}")
PYRepository: larksuite/cli
Length of output: 919
Remove the unused priority map declaration and initialization.
The priority variable is populated in a loop but never read. Go rejects unused locals, so this code will not compile until these lines are removed.
Suggested fix
- priority := make(map[string]int, len(order))
- for i, k := range order {
- priority[strings.ToLower(k)] = i + 1
- }
sorted := make([]xhtml.Attribute, 0, len(n.Attr))
used := make([]bool, len(n.Attr))📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| priority := make(map[string]int, len(order)) | |
| for i, k := range order { | |
| priority[strings.ToLower(k)] = i + 1 | |
| } |
🤖 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/mail/lint/linter.go` around lines 735 - 738, The code declares and
populates a local map variable named `priority` (created with
make(map[string]int, len(order)) and filled in the loop `for i, k := range order
{ priority[strings.ToLower(k)] = i + 1 }`) but never uses it; remove the unused
`priority` declaration and the entire loop that fills it to fix the unused-local
compile error, or if the map is intended to be used later, replace its usage
appropriately where `priority` is referenced instead of deleting—ensure no other
code expects `priority` after removal.
| // Strip leading whitespace + control bytes that could obscure the | ||
| // scheme (e.g. "java\tscript:..."). The html-parser already strips | ||
| // stray whitespace at attribute boundaries; this is defence-in-depth | ||
| // for older clients that paste from Word with U+0009 / U+0020 inside | ||
| // the scheme prefix. | ||
| value = strings.Map(func(r rune) rune { | ||
| if r < 0x20 || r == 0x7F { | ||
| return -1 | ||
| } | ||
| return r | ||
| }, value) | ||
|
|
||
| // Find the colon delimiter; everything before it is the scheme. | ||
| colon := strings.IndexByte(value, ':') | ||
| if colon < 0 { | ||
| // No scheme → relative URL → allow. | ||
| return "ok", "" | ||
| } | ||
| scheme := strings.ToLower(value[:colon]) | ||
| rest := value[colon+1:] | ||
|
|
||
| switch { | ||
| case allowedURLSchemes[scheme]: | ||
| return "ok", "" | ||
| case scheme == "data": | ||
| // data:image/* is whitelisted; anything else (e.g. data:text/html;...) | ||
| // is rejected. The check tolerates any subtype under image/* (png / | ||
| // jpeg / gif / svg+xml / webp) so users embedding base64 thumbnails | ||
| // don't trip the rule. | ||
| rest = strings.TrimSpace(rest) | ||
| if strings.HasPrefix(strings.ToLower(rest), "image/") { | ||
| return "ok", "" | ||
| } | ||
| return "error", RuleAttrJSURLBlocked | ||
| case blockedURLSchemes[scheme]: | ||
| return "error", RuleAttrJSURLBlocked | ||
| default: | ||
| // Unknown scheme: surface a warning so users see it but don't | ||
| // drop legitimate webcal:/tel: / similar in case downstream | ||
| // renders eventually support them. | ||
| return "warn", RuleAttrUnsafeSchemeBlocked |
There was a problem hiding this comment.
Strip plain spaces from the scheme prefix before classification.
The hardening comment says U+0020 inside the scheme should be ignored, but this mapper only removes bytes < 0x20. Inputs like href="java script:alert(1)" therefore fall into the unknown-scheme path instead of the blocked-scheme path.
🤖 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/mail/lint/rules.go` around lines 201 - 241, The scheme-cleaning
step currently removes control bytes but leaves plain spaces (U+0020), so inputs
like "java script:..." are misclassified; update the rune filter in the
strings.Map call (the block that assigns value by mapping runes) to also drop
U+0020 (space) so the subsequent colon search and scheme extraction (scheme :=
strings.ToLower(value[:colon])) treat embedded spaces as removed; ensure this
change still preserves removal of control bytes and DEL and keeps the rest of
the logic around allowedURLSchemes, blockedURLSchemes and the "data" handling
intact.
| var showLintDetailsFlag = common.Flag{ | ||
| Name: "show-lint-details", | ||
| Type: "bool", | ||
| Desc: "Include the full lint_applied[] / original_blocked[] arrays in the envelope. Default: only counts (lint_applied_count / original_blocked_count) are returned to keep the envelope small.", | ||
| } |
There was a problem hiding this comment.
Update --show-lint-details help text to match current behavior.
Line 22 says default mode returns counts, but applyLintToEnvelope (Line 79-84) now omits all four lint fields unless --show-lint-details=true. The flag description should be updated to avoid misleading users.
✏️ Suggested text fix
var showLintDetailsFlag = common.Flag{
Name: "show-lint-details",
Type: "bool",
- Desc: "Include the full lint_applied[] / original_blocked[] arrays in the envelope. Default: only counts (lint_applied_count / original_blocked_count) are returned to keep the envelope small.",
+ Desc: "Include lint details in the envelope: lint_applied_count, original_blocked_count, lint_applied[], and original_blocked[]. Default: these fields are omitted to keep the envelope small.",
}🤖 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/mail/mail_lint_writepath.go` around lines 19 - 23, The help text
for the flag showLintDetailsFlag is misleading: update its Desc to state that
lint fields are omitted by default and only included when
--show-lint-details=true to match applyLintToEnvelope behavior; edit the Desc
for the variable showLintDetailsFlag to clearly say that the four lint fields
(lint_applied, original_blocked and their counts) are omitted unless the flag is
true, referencing the applyLintToEnvelope logic that controls inclusion.
| <ul data-list-bullet="true" style="margin-top:0px;margin-bottom:0px;margin-left:0px;padding-left:0px;list-style-position:inside"><ul data-list-bullet="true" style="margin:0px 0px 0px 24px;padding-left:0px;list-style-position:inside"><li class="temp-li bullet2" data-li-line="true" data-list="bullet2" style="line-height:1.6;margin-top:2px;margin-bottom:2px;padding-left:0px;display:list-item;list-style-type:circle;font-family:inherit;font-size:14px;margin-left:0px;list-style-position:inside" dir="auto"><span style="font-family:inherit"><span style="color:rgb(31,35,41)">[工作职责描述 1:聚焦动作 + 产出,附数据 / 影响范围]</span></span></li><li class="temp-li bullet2" data-li-line="true" data-list="bullet2" style="line-height:1.6;margin-top:2px;margin-bottom:2px;padding-left:0px;display:list-item;list-style-type:circle;font-family:inherit;font-size:14px;margin-left:0px;list-style-position:inside" dir="auto"><span style="font-family:inherit"><span style="color:rgb(31,35,41)">[工作职责描述 2:核心成果 + 关键技术 / 方法]</span></span></li><li class="temp-li bullet2" data-li-line="true" data-list="bullet2" style="line-height:1.6;margin-top:2px;margin-bottom:2px;padding-left:0px;display:list-item;list-style-type:circle;font-family:inherit;font-size:14px;margin-left:0px;list-style-position:inside" dir="auto"><span style="font-family:inherit"><span style="color:rgb(31,35,41)">[工作职责描述 3]</span></span></li></ul></ul> | ||
| <ol start="2" data-list-number="true" style="margin-top:0px;margin-bottom:0px;margin-left:0px;padding-left:0px;list-style-position:inside"><li class="temp-li number1" data-li-line="true" data-list="number1" data-ol-id="work" data-start="2" style="line-height:1.6;margin-top:4px;margin-bottom:4px;padding-left:0px;display:list-item;list-style-type:decimal;font-family:inherit;font-size:14px;margin-left:0px;list-style-position:inside" dir="auto"><b><span style="font-family:inherit"><span style="color:rgb(31,35,41)">[公司名称]</span></span></b><span style="font-family:inherit"><span style="color:rgb(31,35,41)"> · [职位] · [全职 / 实习 / 兼职]</span></span><span style="font-family:inherit"><span style="color:rgb(143,149,158);font-size:13px"> · [YYYY-MM] ~ [YYYY-MM]</span></span></li></ol> | ||
| <ul data-list-bullet="true" style="margin-top:0px;margin-bottom:0px;margin-left:0px;padding-left:0px;list-style-position:inside"><ul data-list-bullet="true" style="margin:0px 0px 0px 24px;padding-left:0px;list-style-position:inside"><li class="temp-li bullet2" data-li-line="true" data-list="bullet2" style="line-height:1.6;margin-top:2px;margin-bottom:2px;padding-left:0px;display:list-item;list-style-type:circle;font-family:inherit;font-size:14px;margin-left:0px;list-style-position:inside" dir="auto"><span style="font-family:inherit"><span style="color:rgb(31,35,41)">[工作职责描述 1]</span></span></li><li class="temp-li bullet2" data-li-line="true" data-list="bullet2" style="line-height:1.6;margin-top:2px;margin-bottom:2px;padding-left:0px;display:list-item;list-style-type:circle;font-family:inherit;font-size:14px;margin-left:0px;list-style-position:inside" dir="auto"><span style="font-family:inherit"><span style="color:rgb(31,35,41)">[工作职责描述 2]</span></span></li></ul></ul> | ||
| <div style="margin-top:24px;margin-bottom:12px;line-height:1.6"><div dir="auto" style="font-size:14px;border-bottom:1px solid rgb(222,224,227);padding-bottom:6px"><b><span style="font-size:16px"><span style="font-family:inherit"><span style="color:rgb(31,35,41)">项目经历</span></span></span></b></div></div> | ||
| <ol start="1" data-list-number="true" style="margin-top:0px;margin-bottom:0px;margin-left:0px;padding-left:0px;list-style-position:inside"><li class="temp-li number1" data-li-line="true" data-list="number1" data-ol-id="proj" data-start="1" style="line-height:1.6;margin-top:4px;margin-bottom:4px;padding-left:0px;display:list-item;list-style-type:decimal;font-family:inherit;font-size:14px;margin-left:0px;list-style-position:inside" dir="auto"><b><span style="font-family:inherit"><span style="color:rgb(31,35,41)">[项目名称]</span></span></b><span style="font-family:inherit"><span style="color:rgb(31,35,41)"> · [角色,如 负责人 / 核心开发 / 设计主导]</span></span><span style="font-family:inherit"><span style="color:rgb(143,149,158);font-size:13px"> · [YYYY-MM] ~ [YYYY-MM]</span></span></li></ol> | ||
| <ul data-list-bullet="true" style="margin-top:0px;margin-bottom:0px;margin-left:0px;padding-left:0px;list-style-position:inside"><ul data-list-bullet="true" style="margin:0px 0px 0px 24px;padding-left:0px;list-style-position:inside"><li class="temp-li bullet2" data-li-line="true" data-list="bullet2" style="line-height:1.6;margin-top:2px;margin-bottom:2px;padding-left:0px;display:list-item;list-style-type:circle;font-family:inherit;font-size:14px;margin-left:0px;list-style-position:inside" dir="auto"><span style="font-family:inherit"><span style="color:rgb(31,35,41)">[项目背景 / 业务价值 1 句话]</span></span></li><li class="temp-li bullet2" data-li-line="true" data-list="bullet2" style="line-height:1.6;margin-top:2px;margin-bottom:2px;padding-left:0px;display:list-item;list-style-type:circle;font-family:inherit;font-size:14px;margin-left:0px;list-style-position:inside" dir="auto"><span style="font-family:inherit"><span style="color:rgb(31,35,41)">[关键贡献 / 技术栈]</span></span></li><li class="temp-li bullet2" data-li-line="true" data-list="bullet2" style="line-height:1.6;margin-top:2px;margin-bottom:2px;padding-left:0px;display:list-item;list-style-type:circle;font-family:inherit;font-size:14px;margin-left:0px;list-style-position:inside" dir="auto"><span style="font-family:inherit"><span style="color:rgb(31,35,41)">[项目成果 / 数据指标]</span></span></li></ul></ul> | ||
| <ol start="2" data-list-number="true" style="margin-top:0px;margin-bottom:0px;margin-left:0px;padding-left:0px;list-style-position:inside"><li class="temp-li number1" data-li-line="true" data-list="number1" data-ol-id="proj" data-start="2" style="line-height:1.6;margin-top:4px;margin-bottom:4px;padding-left:0px;display:list-item;list-style-type:decimal;font-family:inherit;font-size:14px;margin-left:0px;list-style-position:inside" dir="auto"><b><span style="font-family:inherit"><span style="color:rgb(31,35,41)">[项目名称]</span></span></b><span style="font-family:inherit"><span style="color:rgb(31,35,41)"> · [角色]</span></span><span style="font-family:inherit"><span style="color:rgb(143,149,158);font-size:13px"> · [YYYY-MM] ~ [YYYY-MM]</span></span></li></ol> | ||
| <ul data-list-bullet="true" style="margin-top:0px;margin-bottom:0px;margin-left:0px;padding-left:0px;list-style-position:inside"><ul data-list-bullet="true" style="margin:0px 0px 0px 24px;padding-left:0px;list-style-position:inside"><li class="temp-li bullet2" data-li-line="true" data-list="bullet2" style="line-height:1.6;margin-top:2px;margin-bottom:2px;padding-left:0px;display:list-item;list-style-type:circle;font-family:inherit;font-size:14px;margin-left:0px;list-style-position:inside" dir="auto"><span style="font-family:inherit"><span style="color:rgb(31,35,41)">[项目描述 + 关键贡献 + 成果数据]</span></span></li></ul></ul> |
There was a problem hiding this comment.
Fix the invalid nested list structure.
These sections currently render as <ul><ul>...</ul></ul>, but a <ul> may only contain <li> children. Mail renderers repair that markup differently, so bullets and spacing can drift or disappear. Flatten each pair to a single indented list, or nest the inner list inside a real <li>.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@skills/lark-mail/assets/templates/job-application--resume.html` around lines
16 - 23, The template contains invalid nested list wrappers like repeated "<ul
data-list-bullet=\"true\">...<ul data-list-bullet=\"true\">...</ul></ul>" and
"<ol ... data-list-number=\"true\">...<ol ...>...</ol></ol>" which produce
non-LI children; update the HTML so each list wrapper only directly contains
<li> elements by either flattening the pair into a single indented <ul> or <ol>
(remove the outer/inner duplicate wrapper), or move the inner list inside a real
<li> (so the inner "<ul>" becomes a child of a corresponding "<li
class=\"temp-li ...\">"); ensure instances with class="temp-li bullet2" and
data-ol-id="work"/"proj" follow this rule and keep styling attributes on the
remaining valid list elements.
|
|
||
| **正文编辑和其他高级操作必须通过 `--patch-file`**。没有 `--set-body` flag。 | ||
|
|
||
| **CRITICAL - 编辑邮件内容前 MUST 先用 Read 工具读取 [references/lark-mail-html.md](references/lark-mail-html.md),其中包含邮件书写规范** |
There was a problem hiding this comment.
Fix the relative link target in the new CRITICAL note.
Line 15 links to references/lark-mail-html.md, which is incorrect from this directory and likely broken. Use a same-directory relative link instead.
🔗 Suggested fix
-**CRITICAL - 编辑邮件内容前 MUST 先用 Read 工具读取 [references/lark-mail-html.md](references/lark-mail-html.md),其中包含邮件书写规范**
+**CRITICAL - 编辑邮件内容前 MUST 先用 Read 工具读取 [lark-mail-html.md](lark-mail-html.md),其中包含邮件书写规范**📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| **CRITICAL - 编辑邮件内容前 MUST 先用 Read 工具读取 [references/lark-mail-html.md](references/lark-mail-html.md),其中包含邮件书写规范** | |
| **CRITICAL - 编辑邮件内容前 MUST 先用 Read 工具读取 [lark-mail-html.md](lark-mail-html.md),其中包含邮件书写规范** |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@skills/lark-mail/references/lark-mail-draft-edit.md` at line 15, The CRITICAL
note currently links to "references/lark-mail-html.md" which is incorrect for
this directory; update the Markdown link target in the note (the string
"references/lark-mail-html.md") to a same-directory relative path such as
"./lark-mail-html.md" (or "lark-mail-html.md") so the link resolves correctly
from skills/lark-mail/references/lark-mail-draft-edit.md.
| 编辑邮件内容前 MUST 先用 Read 工具读取 [references/lark-mail-html.md](references/lark-mail-html.md),其中包含邮件书写规范** | ||
|
|
There was a problem hiding this comment.
Fix bold markdown formatting.
Line 17 ends with ** but there's no opening ** at the start of line 16.
📝 Proposed fix
-编辑邮件内容前 MUST 先用 Read 工具读取 [references/lark-mail-html.md](references/lark-mail-html.md),其中包含邮件书写规范**
+**编辑邮件内容前 MUST 先用 Read 工具读取 [references/lark-mail-html.md](references/lark-mail-html.md),其中包含邮件书写规范**📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| 编辑邮件内容前 MUST 先用 Read 工具读取 [references/lark-mail-html.md](references/lark-mail-html.md),其中包含邮件书写规范** | |
| **编辑邮件内容前 MUST 先用 Read 工具读取 [references/lark-mail-html.md](references/lark-mail-html.md),其中包含邮件书写规范** |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@skills/lark-mail/references/lark-mail-forward.md` around lines 16 - 17, The
markdown has an unmatched closing bold marker in the sentence "编辑邮件内容前 MUST 先用
Read 工具读取
[references/lark-mail-html.md](references/lark-mail-html.md),其中包含邮件书写规范**" — fix
by removing the trailing "**" or by adding a matching opening "**" before the
text you intend to bold (update the line containing that sentence in
lark-mail-forward.md so bold markers are balanced).
| 编辑邮件内容前 MUST 先用 Read 工具读取 [references/lark-mail-html.md](references/lark-mail-html.md),其中包含邮件书写规范** | ||
|
|
There was a problem hiding this comment.
Fix bold markdown formatting.
Line 17 ends with ** but there's no opening ** at the start of line 16.
📝 Proposed fix
-编辑邮件内容前 MUST 先用 Read 工具读取 [references/lark-mail-html.md](references/lark-mail-html.md),其中包含邮件书写规范**
+**编辑邮件内容前 MUST 先用 Read 工具读取 [references/lark-mail-html.md](references/lark-mail-html.md),其中包含邮件书写规范**📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| 编辑邮件内容前 MUST 先用 Read 工具读取 [references/lark-mail-html.md](references/lark-mail-html.md),其中包含邮件书写规范** | |
| **编辑邮件内容前 MUST 先用 Read 工具读取 [references/lark-mail-html.md](references/lark-mail-html.md),其中包含邮件书写规范** |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@skills/lark-mail/references/lark-mail-reply-all.md` around lines 16 - 17, The
markdown line containing "编辑邮件内容前 MUST 先用 Read 工具读取
[references/lark-mail-html.md](references/lark-mail-html.md),其中包含邮件书写规范**" has
an unmatched closing bold marker; fix it by either removing the trailing "**" or
adding a matching opening "**" before the intended bold text so the Markdown is
valid—update the sentence in lark-mail-reply-all.md accordingly to use proper
bold markers.
| 编辑邮件内容前 MUST 先用 Read 工具读取 [references/lark-mail-html.md](references/lark-mail-html.md),其中包含邮件书写规范** | ||
|
|
There was a problem hiding this comment.
Fix bold markdown formatting.
Line 21 ends with ** but there's no opening ** at the start of line 20.
📝 Proposed fix
-编辑邮件内容前 MUST 先用 Read 工具读取 [references/lark-mail-html.md](references/lark-mail-html.md),其中包含邮件书写规范**
+**编辑邮件内容前 MUST 先用 Read 工具读取 [references/lark-mail-html.md](references/lark-mail-html.md),其中包含邮件书写规范**📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| 编辑邮件内容前 MUST 先用 Read 工具读取 [references/lark-mail-html.md](references/lark-mail-html.md),其中包含邮件书写规范** | |
| **编辑邮件内容前 MUST 先用 Read 工具读取 [references/lark-mail-html.md](references/lark-mail-html.md),其中包含邮件书写规范** |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@skills/lark-mail/references/lark-mail-reply.md` around lines 20 - 21, The
markdown line ending with an unmatched bold delimiter ("编辑邮件内容前 MUST 先用 Read
工具读取 [references/lark-mail-html.md](references/lark-mail-html.md),其中包含邮件书写规范**")
has a stray closing **; fix by either removing the trailing ** or adding a
matching opening ** before the intended bolded text so the bold markup is
balanced (edit the sentence text fragment shown to ensure matching **
delimiters).
| 编辑邮件内容前 MUST 先用 Read 工具读取 [references/lark-mail-html.md](references/lark-mail-html.md),其中包含邮件书写规范** | ||
|
|
There was a problem hiding this comment.
Fix bold markdown formatting.
Line 16 ends with ** but there's no opening ** at the start of line 15, resulting in malformed bold text.
📝 Proposed fix
-编辑邮件内容前 MUST 先用 Read 工具读取 [references/lark-mail-html.md](references/lark-mail-html.md),其中包含邮件书写规范**
+**编辑邮件内容前 MUST 先用 Read 工具读取 [references/lark-mail-html.md](references/lark-mail-html.md),其中包含邮件书写规范**📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| 编辑邮件内容前 MUST 先用 Read 工具读取 [references/lark-mail-html.md](references/lark-mail-html.md),其中包含邮件书写规范** | |
| **编辑邮件内容前 MUST 先用 Read 工具读取 [references/lark-mail-html.md](references/lark-mail-html.md),其中包含邮件书写规范** |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@skills/lark-mail/references/lark-mail-send.md` around lines 15 - 16, The line
"编辑邮件内容前 MUST 先用 Read 工具读取
[references/lark-mail-html.md](references/lark-mail-html.md),其中包含邮件书写规范**" has a
stray closing bold marker (**); fix the malformed bold by either removing the
trailing "**" or by adding a matching opening "**" before the text you intend to
bold so the markdown becomes balanced—update the sentence in the
lark-mail-send.md content accordingly.
|
|
Generated by the harness-coding skill.
Sprints
What changed
Bug A —
shortcuts/mail/mail_lint_writepath.go: moveddata["lint_applied_count"] = len(applied)anddata["original_blocked_count"] = len(blocked)inside the existingif showDetails {}block so all 4 lint fields (2 counts + 2 arrays) follow the same gating. This aligns the implementation with tech-design §4.1.5's "same in, same out" rule and the default-3-keys envelope contract.Bug B —
shortcuts/mail/helpers.go: removed thetipentry frombuildDraftSavedOutput's returned map. The same teaching string is still emitted on stderr byhintSendDraft(see mail_send.go:306 et al.), so UX is unchanged. Test assertions inmail_send_confirm_output_test.goare flipped to requiretipabsent.Source specs
This MR was created autonomously. Quality gates were enforced by the repo's own pre-commit hooks.
Summary by CodeRabbit
New Features
+lint-htmlcommand for local HTML validation with optional auto-fix and strict-mode checking.+draft-create,+draft-edit,+forward,+reply,+reply-all,+send) to automatically sanitize unsafe markup and normalize legacy tags.--show-lint-detailsflag to display detailed lint findings in command output.Documentation
+lint-htmlusage documentation and guidelines for compose operations.