references/durable-context.md
# Durable Context Preflight
Shared preamble for every skill that reads optional memory or prior-decision context. Each `SKILL.md` links to this file and then adds skill-specific guidance.
## Scope
Read durable context when the user names memory, a prior decision, or a memory path, or when the project exposes an obvious local memory summary (a `MEMORY.md` or a documented memory directory). List titles first and open at most one or two summaries; do not hard-code machine-specific memory roots, and do not read raw transcripts. Treat cross-project entries as transferable patterns, not as facts about this project.
## Current state wins
Current code, diff, screenshots, logs, tests, docs, CI, remote state, and live probes always override memory, including memory the runtime injects on its own. A remembered fact is a lead to re-verify, never evidence. When current state conflicts with a remembered claim, name the conflict and follow current state.
## Memory is not authorization
Memory may explain preferences, but it must never grant or broaden authorization for writes, commits, pushes, publishing, public replies, deletion, or other state changes. Current-turn instructions and current project rules decide authorization. Historical phrases such as `push` or `check` are context to re-evaluate, not reusable action tokens.
## Redaction gate
When turning prior chats, durable memory, or cross-project notes into reusable Waza guidance, promote only workflow rules. Strip raw transcript text, screenshots, local paths, project-specific commands, issue or PR numbers, release tags, commit hashes, private product boundaries, paid or license details, support routing, user names, and one-machine state.
If an example is necessary, use neutral placeholders such as `ExampleCLI`, `ExampleApp`, `<issue>`, `<release>`, or `<command>`. Do not copy a private answer, maintainer reply, screenshot observation, or project-specific incident as a durable rule.
The skill-specific overrides and constraints live in each `SKILL.md`, in the paragraph that follows its link to this file.
references/mode-long-form.md
# Long-form Article Mode
Loaded from `write` when the input is a long draft that needs structural work before line edits.
Activate when: a long article needs structural review. Multiple headings or images alone do not authorize restructuring.
Read the whole article to distinguish useful explanation from repeated conclusions, table re-reads and generic endings. Preserve the strongest version of a supported point. Do not assume a long article is padded or set a target fraction to delete.
Workflow:
1. **Map first, read-only.** Before editing anything, read the whole article and list every `##` section, table, list, and image. Flag three structural problems: cross-section repetition (same checklist / judgment list / core claim in 2+ sections), table re-reading (a section whose prose walks the rows of the table above it), and whole redundant sections or paragraphs.
2. **Test the paragraph's contribution.** Experience, emotion, personal conviction, qualifications and explanations can all carry a paragraph. Lack of a new fact or source is not grounds for cutting it. Check whether an edit loses the author's stance or emotional intensity, as well as factual meaning. A shorter, more neutral draft can be a worse one.
3. **Respect the editing scope.** Read-only requests get proposed change-points. Explicit rewrite requests authorize edits within that scope; do not ask again for routine sentence cuts. Name whole-paragraph cuts in the diff summary, and ask before deleting a section or reorganizing headings unless that structural work was requested.
4. **Then line-level de-AI**, section by section, using the relevant language references. For mirrors, check both meaning and native rhythm.
5. **Return the requested artifact.** For repository edits, preserve frontmatter, code, media, links and substantive explanations, run the site's build, and report the scoped diff. A pasted-text rewrite returns the prose. Run the Punctuation Gate on edited prose without changing technical examples to satisfy a style rule.
Do not single-pass rewrite a long article: it silently overwrites the author's hand-tuned phrasing and cannot be reviewed as a diff. See `write-zh.md` 结构级重复与表格复读(长文专项)for the matching content rules.
references/mode-public-reply.md
# Public Reply Mode (GitHub issue / PR)
Loaded from `write` when the deliverable is a maintainer reply on a public issue or PR thread.
Activate when: "回复 issue", "reply to PR", "comment on #N", "回 issue", or the user asks for the text of a GitHub issue / PR comment.
Hard rules for the reply body:
1. **Default to one paragraph and one or two sentences.** Open with `@<reporter>` and at most one short thanks. Match the reporter's language. No exclamation mark or stacked courtesy ending.
2. **State the factual boundary and the reporter's next step.** Name exactly one ship state: already shipped in v<X.Y.Z>, fixed on `main` and going out in the next release, planned for v<X.Y.Z>, not planned with one-line reason and an alternative path, or still needs specific evidence. Include root cause only when it changes what the reporter should do. Internal symbols, files, CI approval, and maintainer process stay out.
3. **Every sentence must be currently true.** No "already shipped" without release evidence in the current turn, no "landed on main" while the change sits uncommitted, and no implied build or artifact verification that did not happen.
4. **Two short paragraphs are the exception**, used only when a one-line command or necessary ambiguity cannot fit cleanly. No bullet lists, section headers, or code blocks except that one-line command.
5. **A batch of replies is N replies, not one skeleton filled N times.** When closing or answering several threads in one pass, read the drafts side by side before posting: same opening clause, same paragraph order, and same closing move across three or more of them reads as template voice no matter how correct each one is. Only the facts are shared. The opening sentence in particular should come from that thread's own report.
The reply is the final user-facing text, not an agent log. Do not write "刚才我判断错了", "前面回复有误", "I re-read it and changed the comment", or any meta narration about your own process. If editing an existing maintainer comment, replace it with the clean final wording as if it were the only comment the user will read.
Before posting, re-read the live issue / PR with `gh issue view <num>` or `gh pr view <num>`. Do not reply from memory; titles, states, and author languages change between sessions.
After posting or editing, re-read the comment body, author, target item, and issue/PR state. Until that readback matches the intended final text, the public action is not done.
For paid / subscribed users, acknowledge the purchase relationship and the inconvenience in one phrase, then state the boundary. Do not over-explain. When the current product cannot support their setup, suggest the safest practical path (upgrade macOS, wait for the next release, provide logs, refund route) without arguing.
For private support channels (DM, in-app reply, support email), drop the report register entirely: short colloquial sentences in the maintainer's own voice, lead with what the user gets rather than how it works, and fewer full stops than documentation would carry.
Closing rule: when closing as `completed`, the comment must independently explain what was fixed and the expected release. When closing as `not planned`, the comment must independently explain the current boundary and an alternative path. Do not rely on prior thread context as the explanation.
references/mode-release-notes.md
# Release Note Template Mode
Loaded from `write` when the ask is a release note, changelog entry, or update-feed copy.
Activate when: "release", "changelog", "version", "release notes"
Format: target-project style by default. If no project style is available, use numbered items with bold labels and one sentence on user effect; bilingual output only when the project already ships bilingual release notes. Call out breaking changes and deprecations explicitly when present.
### Release Notes Pre-flight
Before drafting, gather style references:
1. Read the target project's `CLAUDE.md` for its Release Convention / Release Flow section.
2. Read the target project's existing release source as a format, tone, sentence-length, and density reference: changelog, release notes, registry page, update feed, or platform release page.
3. For GitHub projects, `gh release view --json body -R <owner>/<repo>` is the preferred way to read the most recent release when `gh` is available. If the project is not on GitHub, use the release source named by the project docs or user request.
4. If the user mentions comparing with a sibling project's release style, ask for the target identifier or release URL before fetching it.
5. Match the reference release's format, sentence length, and tone. Treat its item count as history, not a quota: the current release may need fewer or more items.
6. Keep each release-note item to one sentence unless the reference project clearly does otherwise. Do not add emoji to release prose unless the target surface is explicitly a reaction or celebratory social surface.
### Release Notes Content Rules
- **Freeze the artifact boundary before drafting.** Resolve the last published release and the exact candidate users will receive. Use `HEAD` only when the candidate is built from `HEAD`; include dirty or generated changes only when they will ship. A later commit is not release-note material for an older artifact.
- **Build a complete user-visible inventory.** For each candidate change, name the intended reader, what was different before and after, and whether the reader can see the result or must act on it. Omit delivery, refactoring, observability, and security mechanics unless they change a visible outcome or require user action. State deliberate omissions in the working notes, not in the published prose.
- **Let outcomes determine the item count.** Use the smallest set of distinct user outcomes that still covers the candidate artifact. Merge changes that serve the same user goal; never split one outcome or retain internal detail to imitate the previous release's count.
- **Group by user-perceivable feature**, not by internal taxonomy. "Polish", "细节打磨", "Misc improvements", "Chores" are not categories users can act on. Group by product surface (Clean / Uninstall / Status / Settings) or by user-visible verb (Faster startup / New keyboard shortcut / Fixed crash on M3).
- **Extract from `git log <last-published>..<candidate>`** rather than from memory. Read every `feat:` and `fix:` commit inside the artifact boundary; do not omit small items just because they look minor in commit form (iOS wrapper support, Dock cleanup, AV-vendor protection boundary are not "minor" from a user point of view).
- **One sentence per item, naming the user-visible change**, not the implementation. The label and opening clause should tell a scanning reader what changed without requiring the rest of the sentence. "Use `CKDownloadQueue` observer for App Store updates" is not a release note; "App Store updates now run inside the app instead of opening App Store" is. Keep technical terms only when the intended reader uses them to recognize a feature, configure it, or act; an internal symbol or rule name is never the item.
- **Bilingual structure**: when the project ships bilingual release notes, put the English block and the Chinese block as two parallel sections inside the same release item; do not interleave per bullet. For HTML-capable update-feed CDATA, separate language blocks with headings so the rendered update window does not collapse them together.
- **Settle structure before localization.** Approve the source-language outcomes, order, and labels before translating. Every locale then preserves the same item count and order while using native register rather than mirroring source-language syntax.
- **Punctuation**: Chinese full-width in Chinese blocks, ASCII in English blocks.
references/write-en.md
> **How to use this file**: it is a catalog of smells, not a checklist to run top to bottom. The principles in `SKILL.md` Core Stance apply here too: over-editing is failure, the author's voice and genre win, and these lists are examples, not find-and-replace. A sentence that already reads natural stays. Match the smell, not the word. One trope used once may be fine; several together, or one repeating, is the smell.
## English Scenario
Eliminate predictable AI writing patterns. Write like a human: varied, imperfect, specific.
### Core Rules
1. **Cut filler phrases.** Remove throat-clearing openers ("It's worth noting", "Notably,", "Importantly,"), emphasis crutches, and adverbs that only signal emphasis. Keep adverbs that carry real meaning.
2. **Break formulaic structures.** Avoid binary contrasts, negative listings, dramatic fragmentation, rhetorical setups, false agency.
3. **Use concrete subjects.** People, software and physical objects can all do things. Remove agency that obscures who acted or how something works; do not force a human subject into every technical sentence.
4. **No vague declaratives.** "The reasons are structural", "The reality is simpler", "History is clear": name the specific thing and show the evidence before the conclusion. No lazy extremes ("every," "always," "never") doing vague work.
5. **Preserve viewpoint.** An author's experience stays "I"; translating it as "you" can turn a personal account into unsolicited advice.
6. **Keep natural rhythm.** Repair fragment-heavy or repetitive prose without quotas for sentence length, list size or paragraph endings.
7. **Trust readers.** Cut hand-holding, but keep uncertainty, attribution and conditions that limit a claim.
8. **Preserve emotional meaning.** A personal conviction or heartfelt ending can sound quotable and still belong. Keep its warmth and force; cut added stock morals, not the author's feelings. Translation must carry the emotion as well as the facts.
9. **Do not replace one formula with another.** “Human” does not mean slangy, quirky, or performatively casual.
10. **No emoji.** Remove any emoji from the text being edited.
### Word Choice
Examples, not exhaustive -- any word used to signal importance rather than to say something is suspect.
**Overused emphasis adverbs (cut these when they only signal importance, not every adverb):**
"quietly", "deeply", "fundamentally", "remarkably", "arguably", "certainly", "really", "just", "literally", "genuinely", "honestly", "simply", "actually"
> NO: "quietly orchestrating workflows"
> OK: Say what it does. Drop the adverb.
**AI vocabulary: replace with plain language:**
| Avoid | Use instead |
|-------|-------------|
| delve (into) | examine, look at, explore |
| leverage (as verb) | use |
| utilize | use |
| robust | strong, reliable, solid |
| streamline | simplify, cut |
| harness | use, apply |
| navigate (challenges) | handle, address |
| unpack | explain, examine |
| paradigm | system, approach, model |
| synergy | combination, cooperation |
| ecosystem | community, network, field |
| tapestry | mix, combination |
| landscape | situation, field, area |
| game-changer | significant, important |
| deep dive | analysis, examination |
| moving forward | next, from now |
**Pompous copulas: use "is" instead:**
> NO: "serves as", "stands as", "marks", "represents"
> OK: "is"
### Sentence Structures to Avoid
Examples, not exhaustive -- any construction that performs insight rather than delivers it belongs here.
**Negative parallelism**: the single most common AI tell:
> NO: "It's not bold. It's backwards." / "Not because X, but because Y." / "The question isn't X. The question is Y." / "Not a bug. Not a feature. A fundamental design flaw."
> OK: State Y directly. Drop the negation entirely.
**Rhetorical self-questions:**
> NO: "The result? Devastating." / "The worst part? Nobody saw it coming."
> OK: State it: "The result was devastating."
**Anaphora abuse**: repeating the same sentence opener:
> NO: "They assume that... They assume that... They assume that..."
> OK: Combine or restructure. One statement, clear subject.
**Tricolon abuse**: rule of three, used three times back to back:
> NO: "Products impress people; platforms empower them. Products solve problems; platforms create worlds." / "Apple didn't build Uber. Facebook didn't build Spotify. Stripe didn't build Shopify."
> OK: Make the point once, cleanly. One example is enough.
**False ranges:**
> NO: "From innovation to cultural transformation" (what's in between?)
> OK: List the two things directly, or pick one.
**Dramatic fragmentation**: manufactured emphasis via fragments, inside a sentence or as whole paragraphs:
> NO: "He published this. Openly. In a book. As a priest." / "These weren't just products. And the software side matched. Then it professionalised."
> OK: Complete sentences. Trust content over presentation.
**False agency**: a metaphor substitutes for the actual mechanism:
> NO: "The data wants a cleaner narrative."
> OK: Name what the data supports or who made the decision. "The tool reads the file" is ordinary technical English and stays.
### Tone Patterns to Avoid
**False suspense transitions:**
> NO: "Here's the kicker." / "Here's the thing." / "Here's where it gets interesting."
> OK: Make the point. No buildup.
**Patronizing analogies:**
> NO: "Think of it like a highway system for data." / "Think of it as a Swiss Army knife."
> OK: Explain the concept directly. If an analogy helps, test it against three criteria before keeping it: (1) remove it and the paragraph collapses: it is load-bearing, not decorative; (2) push it one layer deeper and it still holds; (3) the reader gets it without further explanation. If it fails any of these, drop the analogy and state the idea directly.
**Futurist invitation:**
> NO: "Imagine a world where every tool you use has a quiet intelligence behind it..."
> OK: Describe what actually exists or what you're actually proposing.
**False vulnerability**: performative self-awareness:
> NO: "And yes, I'm openly in love with the platform model"
> OK: Real vulnerability is specific and uncomfortable. Skip the polish.
**Grandiose stakes inflation:**
> NO: "This will fundamentally reshape how we think about everything." / "will define the next era of computing"
> OK: Say what it actually does.
**Pedagogical hand-holding**, including the sentence that announces a list or classification:
> NO: "Let's break this down step by step." / "Let's unpack what this really means." / "Community data is an interesting piece. Diversity has to cover..." / "One detail is easy to miss. 'Output action' can mean..."
> OK: Start with the content: "Community data needs to cover different lighting, camera angles and rooms."
**Vague attributions:**
> NO: "Experts argue..." / "Industry reports suggest..." / "Observers have cited..."
> OK: Name the expert, link the report, quote the person. If you can't, you don't have a source.
**Invented concept labels**: compound labels that sound analytical but aren't grounded:
> NO: "the supervision paradox" / "the acceleration trap" / "workload creep"
> OK: Describe the thing directly. Don't name it as if it's an established term.
### Paragraph & Composition Patterns to Avoid
**Bold-first bullets**: every bullet starts with a bolded phrase:
> NO: "**Security**: Environment-based configuration..." / "**Performance**: Lazy loading..."
> OK: Write bullets as sentences, or drop the bold. Not every list needs labels.
**Fractal summaries**: "what I'm going to tell you; what I'm telling you; what I just told you":
> NO: "In this section, we'll explore... [3000 words later] ...as we've seen in this section." / "In conclusion..." / "To sum up..."
> OK: Skip the preview and the recap. Write the content, then end without announcing the ending.
> Exception: a single closing sentence in a TL;DR that orients non-specialist readers to the structure ahead ("The rest of this piece follows X in order...") is intentional navigation, not a fractal summary. Keep it; flag it at most, but do not delete without author approval.
**The dead metaphor**: one metaphor used 10 times across a piece:
> NO: "The ecosystem needs ecosystems to build ecosystem value."
> OK: Use a metaphor once, then move on.
**One-point dilution**: same argument restated 10 ways across 4000 words:
> OK: Say it once. Add evidence or move on.
**"Despite its challenges..." formula:**
> NO: "Despite these challenges, the initiative continues to thrive."
> OK: Either address the challenges or don't raise them.
**Meta figure and diagram explanations:**
> NO: "This diagram lists the sensor stack of a humanoid robot. With it in view, the previous problems become easier to place."
> NO: "I made this diagram with ChatGPT Image2. Seeing the representations side by side makes the differences easier to grasp."
> NO: "This timeline shows the evolution... That is one of the reasons I find this field more interesting..."
> OK: The image or timeline itself. Let the surrounding judgment or personal project anchor carry the weight. Keep creation-process details only if they are themselves part of the story.
**Re-anchoring after cutting recaps (long-form articles):**
Keep concrete project details when cutting recaps, but do not add another callback to the same project at every section ending. A callback earns its place only when it adds a consequence or limitation not already explained.
references/write-product-localization.md
# Product Localization Copy Review
Use this when reviewing product pages, release notes, app strings, runtime notifications, appcast or update feeds, docs/help pages, legal/privacy copy, and other localized product surfaces.
## Core Principles
1. **Split surfaces before editing.** A release feed, website page, runtime catalog, help article, and legal page may intentionally support different locale sets. Do not force every surface to mirror the broadest one.
2. **Preserve intent and verified facts.** Keep versions, dates, links, placeholders, shortcuts, identifiers, and legal obligations stable. The source language is not factual authority: when a behavior claim is disproved, flag it in review or correct it across the authorized surfaces. Never invent a new product policy to make a sentence sound better.
3. **Use source files, not generated output, as the edit target.** Patch generated pages only when the project explicitly treats them as source. Otherwise find the template, locale JSON, string catalog, or content partial and rebuild.
4. **Review the final rendered or generated surface.** A translation can look fine in a source file but break in a button, menu, release feed, notification, or generated HTML page.
5. **Do not polish into generic marketing.** Native localization means the sentence sounds like a local product, not like a fluent sales page.
## High-Signal Failure Patterns
- **Chinese**: Literal possessives such as "你的 Mac" or "你的设备" when plain "Mac" or "本机" is enough; machine-output verbs such as "检测到" when a result sentence would read better; mixed punctuation; English words with stable Chinese equivalents. Character-level half/full-width punctuation and CJK/Latin spacing are checked by `check-punctuation.sh`; this list keeps the locale-voice judgment calls.
- **Traditional Chinese**: Mainland phrasing copied into Traditional copy; stale locale URLs; words that feel mainland-specific or overly colloquial for the target audience.
- **Japanese**: English noun compounds translated too tightly; missing spaces around product terms when the project style uses them; UI strings that sound like a manual instead of a Mac app.
- **Korean**: Inconsistent platform terms, especially menu bar / menu item wording; overly literal second-person sentences.
- **German**: ASCII fallbacks such as `fuer`, `Pruef`, `Eintraege`, `Menue`, `Luefter`; English developer nouns like "binary" in user-facing copy.
- **Spanish**: Missing accents such as `gestion`, `analisis`, `menus`, `suscripcion`; mechanical replacements that create invalid forms like `actualizaciónes`.
- **French**: Missing apostrophes or accents such as `L app`, `memoire`, `desinstallation`, `defaut`; spaces before punctuation should follow French conventions when the surrounding text already does.
- **Italian**: Missing accents and articles such as `piu`, `non e`, `un app`; mechanical replacements that create invalid forms like `puòi`.
## Surface Voice Defects
Language-agnostic shapes that survive translation review because each locale reads as correct. Check them on the source string first, then on every locale.
- **Parenthetical padding**: the qualifier that got appended in a parenthesis. Titles, labels, and metric names carry none; split the sentence or drop the qualifier.
- **Hedged verdict**: a question mark or a "maybe / possibly" wrapper around a result the product already computed. A verdict sentence states the verdict; the uncertainty belongs in the value, not the punctuation.
- **Untranslated domain noun**: a term borrowed from the implementation used as a metric name or label (ledger, buffer, daemon, quota). Replace it with the word the user would say for the same thing; if there is no such word, the metric is measuring something the user did not ask about.
- **Alarming detail**: a user-facing string that reports the failure mechanism instead of the user's next action. Release notes, error banners, and update prompts keep what the reader does; the mechanism goes in the commit.
## Review Procedure
1. Identify all source and generated surfaces in scope. For websites, include templates, locale JSON, content partials, generated pages, language switchers, canonical links, and route rewrites. For apps, include runtime catalogs, permission strings, update feeds, and notification copy.
2. Freeze a reading ledger by stable content ID, locale, and surface. Include full bodies, titles, descriptions, tables, FAQs, alts, and captions. Track unread, fully read, open findings, and closed findings separately. Keyword scans, changed-file counts, and truncated reads do not establish full coverage. Close each finding as verified, intentionally retained with a reason, or blocked by named missing evidence.
3. Verify claims against the actual operation, edition, and version: implementation or shipping artifact for product behavior, official documentation for third-party comparisons. A release page establishes what was announced, not proof of every implementation claim. Separate historical statements, hypothetical examples, and measured results. Check conditions hidden by fluent wording: synchronization is not an independent backup, recovery depends on retained data, and a symptom is not a unique diagnosis.
4. Read for local voice and semantic drift. Preserve the author's supplied stories and judgments rather than adding a generic conclusion or sales pitch. Use the platform's actual UI names; translate implementation metaphors into the action the reader performs. Distinguish a permission from a feature toggle, and a temporary mute from disabling a service. Recheck quantifiers and causal words such as all, only, always, and because in every affected locale.
5. Check mechanical artifacts and the surrounding context: accents, plural forms, placeholders, links, and accidental path translations. Then reread metadata against the corrected body so a title or FAQ does not restore a removed guarantee. Inspect images themselves; a correct caption cannot repair an incorrect diagram label, arrow, or additive breakdown of overlapping categories.
6. Rebuild and inspect the final generated surface after the last edit. Confirm actual routes, glyph coverage, wrapping, and dynamic string composition where affected. Reconcile all pending reviewer patches before acceptance. Report reading coverage, structural checks, rendered validation, and deployment separately; do not describe an untested layer as complete. For review-only requests, run only safe diagnostics and list unperformed checks honestly.
## Structural Checks Are Not Editorial Authority
- Preserve unique facts, caveats, figures, commands, and table rows. Compare stable anchors and meaning, not translated line counts or heading list positions; different wrapping and inserted sections make those measurements misleading.
- A useful addition is not a missing-content failure. If a gate demands deleting correct material or copying a broken source structure, investigate the gate. For a misplaced citation, read the claim it supports before moving or duplicating the link.
- Keep executable command bytes intact, but distinguish them from field templates that should be localized. Equal placeholder or key counts cannot catch concatenated sentences, locale-specific separators, or incorrect plurals; render representative outputs.
- Calibrate automated findings with a known defect and a normal example before assigning batch edits. Inspect both strong and weak hits. An English fallback is not a grammar check for other languages; preserve their punctuation conventions, literal file extensions, and technical abbreviations. A clean result proves only the checker's covered rules, not natural voice or factual accuracy.
## Rewrite Rules
- Keep placeholders exactly, including order and type: `%@`, `%d`, `%1$@`, `{name}`, and similar tokens.
- Do not glue translated fragments with punctuation in code or copy. A full sentence or format string per locale is safer.
- Avoid broad find-and-replace unless it is followed by residual scans. Broad accent fixes can produce broken words.
- Leave product names and established UI names in English when the product itself uses them that way.
- Patch the intended fields without reserializing whole catalogs. Preserve adjacent punctuation and unrelated edits; a failed exact-text patch can be a comma mismatch, not proof of concurrent work.
- Treat command examples as inert text. Verify behavior through read-only sources, not by executing cleanup or troubleshooting examples. Use direct file patches rather than shell interpolation that can execute backticks or dollar expressions in prose.
## Output Guidance
For rewrite requests, return the edited localized copy. For review requests, group findings by surface first, then locale. Call out blockers where copy misstates product behavior, privacy, legal terms, version history, or update availability.
references/write-zh-bilingual.md
# 中英双语规则
## 双语一致性检查
逐字符规则(中英之间加空格、CJK 相邻的半角标点改全角、破折号)由门禁脚本 `check-punctuation.sh` 检测;本文只保留脚本不判断的部分。
**术语一致**:同一术语跨全文用同一个形态。首次出现怎么注解、哪些英文词有稳定对译、四类翻译腔套路,见 `write-zh.md` AI 味检测第 15 到 19 条。
**中文里的英文**:标记出现的未翻译英文,建议翻译或加说明。
**双语对**:按段落表达的意思核对,不按行数或字数判优劣。先确认哪份是原稿,再查译文有没有新增经历、替换书或工具、丢掉不确定性、把「我这样做」翻成「你应该」,以及具体例子后多补一段道理。两份都要独立读着自然,不能把中文逗号串句机械搬进英文,也不能为英语节奏添加原文没有的事实。保留适合各语言的链接和例子,涉及作者本人经历与推荐时不能擅自换成当地更熟悉的东西;原稿不清楚就标待确认。
**引号**:方向、是否成对需要人工判断,脚本不检测。
## 双语并列的排版
中英双语 release notes 的排版:
- 英文版在前,中文版在后(或分两个段落)
- 条目编号一一对应
- 专有名词(产品名、功能名)在两种语言里保持一致
- 不要在同一条内中英混写
references/write-zh-release-notes.md
# 对外发文专项:release notes 和推文
## 推文 / 社交发文五规则
适用于已有社区语气和轻量发布节奏的产品工程师项目对外发文:
1. **社区先行**:开头用社会锚点(star 数、感谢用户、谁的反馈推动了这次修复)。改动清单跟在后面,不放在最前。
2. **亮点不全量**:挑 2 到 4 个最有意思的改动。跳过整个模块也没问题。读者要的是故事,不是 changelog。
3. **用户感受帧**:每条写法用"你用它的时候…"或"有一种…的感觉",不是"这个工具做了…"。
4. **一条立场**:至少一句表明决策原因的意见句(例:"我更相信模型本身的能力,而非各种规则限制他的天花板")。
5. **中文节奏**:用地道表达(给留了一手、玩玩、大伙、大概就是这些)。避免翻译腔和正式词(具体判据、主轴、本意)。
结尾用邀请,不用 CTA:「假如没有用过的小伙伴,欢迎去试试看,玩玩。」而不是「立即升级」。
## Release Notes 格式
**结构**:Breaking Changes → New Features → Fixes & Improvements → Deprecations
**格式**:优先匹配目标项目最近一次 release。没有可用参考时,使用编号列表、bold 标签、一句话说用户效果;只有目标项目已经使用中英双语时才输出双语。
**长度参考**:沿用上一个版本的格式、句子长度和信息密度,不把条目数当配额。本次有几个独立的用户结果就写几条,完整说清后立刻停。
**边界**:GitHub Release 正文和社交公告是两份 artifact。Release notes 解释用户会感受到什么,默认不写 CI、tap、registry、API 名称、fallback 路径等机制细节;公告另按社交发文规则挑 2 到 4 个亮点。
## 对外发文专项检查
公开发文交出去之前,扫三件事:
**1. 身份和敏感信号脱敏**:只处理未经授权向目标读者披露的敏感信息。作者明确用于本文或已经公开的身份与经历应保留;披露意图不明确时先确认,不因出现雇主、地点或简历信息就删除。
**2. 不踩竞品**:介绍自己产品时不主动贬低同类产品。直接说自己做了什么、为什么。
**3. 用户感受先于功能清单**:先给一句场景或感受,再进改动细节。不要开场就列功能。
## 发版前检查
- 不出现"不再更新"、"final release"、"停止维护"等终止信号(除非是真实情况)
- release title 简洁:版本号 + 最核心的改动或主题,不超过 10 个词
- 中英版本条目数一一对应
- 条目数取覆盖本次构件所需的最小值,不拆分同一结果、不拿内部实现凑数,每条一句话
references/write-zh.md
> **怎么用这份文件**:它是「认 AI 味」的例子库,不是从头套到尾的检查清单。先用它识味,再凭判断改。自然的句子就别动,**过度改和改不到位一样糟**。作者已有的声音、口语词和体裁选择,优先于任何单条规则。下面所有禁用词、替换表、形式规则(标题、列表、标点),都是「倾向和例子」,不是查找替换。沉淀新教训时并进已有原则,别再加一条禁用词,这份文件不许只增不减。
## 中文场景
### 执行流程(先后顺序)
按下面顺序改,不要反着来:
1. 先识别翻译腔、模板句和重复解释,段末总结也要连着前文读。作者的情绪、价值判断和写作缘由是内容,不能因为抽象、抒情或像金句就删;只修不自然的表达,不把态度改淡。
2. 再顺句子:解决拗口、断句过碎、节奏太硬。
3. 最后调标点:减少不必要的括号、引号和短句句号连发。
4. 术语只在必要处解释:首次出现可注解,后文不重复堆注释。
### 最高优先级:自然 > 风格化
不要为了”像人说话”硬塞口语词、情绪词、俚语、感叹词。
一句话如果已经自然、清楚、稳,就不要再往里加”其实 / 说白了 / 哈哈 / 谁能想到 / 我去 / 居然 / 太太太”这类词。
**保留作者已用的口语词**:如果作者原文已经在用”很”、”其实”、”到底”这类自然词,不要替换成正式词。”最高优先级”是不要主动添加,不是要替换掉已有的。过度去口语化和过度口语化一样糟糕。
### 默认模式:技术长文
技术文默认是工程师对工程师,不是老师训人。少用命令式句子、审问式起手和居高临下语气。
- 可以口语,但不要俚语化
- 减少情绪词、感叹词、口头禅
- 少用”说白了 / 其实 / 你会发现 / 谁能想到”这类推进词
- 不要为了去 AI 味,把技术文改得像口播稿
- 优先做减法:删解释腔、删总结腔、删多余转折,不要把润色做成重写
**受众非工程师时的调整**:如果文章定位是产品 / 业务 / 运营受众,先去三类词:俯视词("不懂技术的同学")、命令腔("你必须 / 一定要")、过深术语("原语 / 未命中 / 非工程团队")。prose 走"我怎么用,你跟着试"路径,不要"系统梳理 + 框架表格 + 长段分析"。
### 开头直接
NO: 随着...的发展
OK: 用了不到一个月后
OK: 作为长期用户,我更关心...
OK: 这次想把 X 这条链路讲清楚
OK: 先说结论,再展开过程
### 用词去正式化
| NO: 不用 | OK: 用 |
|---------|------|
| 非常 / 极其 | 很 |
| 值得注意的是 | 直接说结论 |
| 综上所述 | 直接收尾 |
| 例如 | 比如 |
| 购买 | 买 |
| 使用 | 用 |
| 很多同学 / 不少同学 | 很多人 / 不少人 |
| 这几个事 | 这几件事 |
### 口语词慎用,不要靠它们制造“人味”
下面这些词不是禁用,但默认不要主动加进去:
- 太太太
- 居然
- 谁能想到
- 于是乎
- 我去
- 哈哈
`好比` 只在比喻真的有帮助时偶尔用一次,不要当成固定连接词。
### 生动化:只在原句读不懂时才换
默认不改。只有原句抽象到读者看不懂意思时,才换成更具体的表达。已经清楚的句子不要为”更形象”硬改。
| NO: 过度形象化 | OK: 保持原句或微调 |
|---------|--------|
| 都在偷你的上下文空间 | 都是上下文成本(原句已经清楚) |
| 绝对不能干的事 | 禁止事项(技术文不需要这种语气) |
| 说白了,[结论] | 直接写结论,不加口语引导 |
| X 就是”你怎么知道做对了”这个问题的答案 | 从问题或场景出发描述 X |
| 这就是 X 的力量 | 删掉,让事实说话 |
### 句式
- 不用"首先...其次...最后",用"一方面...另一方面..."
- 段落可以长,但要一口气能读完
- 不用强行断句
- 单独成段的一两句话,多半是上一段的收尾或下一段的引子,直接并进去,别让它孤零零
- 四五个句号连发、每句都很短,读起来像在打电报,这时候把几句合成一个长句,或者用逗号连起来
- 句长和段长相近只提示回读,不是修改理由,结合作者的句读习惯判断有没有刻意排比或断句过碎,不为制造长短变化凑字数。
- 连词按作用取舍,不按比例删。真在转折、接时间、说明因果或带口气的保留,只删按固定位置出现、拿掉也不影响意思的那些。
- 少用“先问问是不是 X”“你得先明白”这类起手,容易有训人感,改成“可以先看 X”“先确认 X”
- 分析型技术文里,少用“第一、第二、第三”模板,除非在写操作步骤
- 三个承重点如果关系很紧,优先写成一段自然段,不必强行拆成列表
### bold 起手 + 句号 节奏
`**xxx**。content` 这种「小标题 + 句号 + 解释」起手段落是 AI 模板感的高频源,**不论密度,默认换成 `**xxx**,content`**,让 bold 变成句子的承重词,而不是独立小标题。整段读起来是 prose,不是工整 bold 列表。
```
NO: **alias**。我在 .zshrc 里加了一行...
OK: **alias**,我在 .zshrc 里加了一行...
```
例外:`**xxx**` 本身就是一个完整句子(如「**能跑不代表安全**」、「**准不准才重要**」),后面跟句号是正常句子结束,不属于"小标题"模式,保留。
判断信号:bold 部分单独看是不是一个能独立成立的句子。是 → 句号;不是(只是个标签 / 短语 / 名词)→ 逗号。
连续 3 行以上的对称 bold 起句即使换成逗号仍是模板节奏,从第 4 个起改成行内嵌入式粗体(句子里加 **重点**)打破。
### 列表去 list 化的渐进策略
技术长文里 bullet 列表过多会显得像 PPT。从 list 走到 prose 的渐进路径:
1. 序号列表 1/2/3 → 改 bullets `- xxx`(去掉序号感)
2. bullets `- **xxx**:content` → 改 bold 段落 `**xxx**,content`(去掉 list marker)
3. 4+ 个 bold 段落 → 合成一个 prose 段落,每项用分号串起来(去掉 visual 切片感)
判断信号:
- 每条都有大段独立解释 → 适合 bold 段落
- 每条 1 句话,关系紧密 → 适合 inline prose 串起来
- 每条是平行规则、读者要扫一眼对照 → 保留 bullets 也行
- 章节标题已经把列表的总结写出来了 → 内容部分尽量 prose 化
序号 1/2/3 留给真正有顺序的:优先级、时间、流程步骤、reference 排序。
默认倾向:判断 / 取舍 / 总结型清单(每条一句、是观点不是步骤)默认转成 prose 段落;速查表 / syllabus(术语密集、读者要逐项对照)和带链接的参考文献保留 list。拿不准时,先转 prose 看读不读得顺。
### 结构级重复与表格复读(长文专项)
后面 AI 味检测第 3 条「重复核心观点」是段落级。长文还有三类只有把整篇摊开横向比对才看得到的结构水分,单段 in-place 润色检测不到:
- **跨章节同义清单**:同一张评估 / 判断 / 取舍清单在 2+ 个 `##` 节里各写一遍(比如「评估机器人看动作频率、数据来源、失败处理」在入门、对比、总结三处都出现)。留信息最全的一份,其余删,或后文一句话带过。
- **表格旁正文复读**:一节里有对比表,正文却一行行复述表里的列。正文只补表给不了的,里程碑、具体例子、判断、为什么,表能讲清的交给表。
- **图 / 表旁文字复述**:紧邻一张图或表的正文,把图 / 表已经列出的项再用文字列一遍,或用「这张图更直观」这类预判句替图说话。删掉复述和预判,留判断和上下文;画图过程只在它本身是叙事时才保留。例外:「图后必要说明段」不算复读,不要删(见 AI 味检测第 13 条后的「例外:图后说明段落不能以"重复"为由删除」)。
判断信号:删掉这段 / 这句,读者会不会丢失一个别处没有的事实,不会就是复读,删;会就保留。
### 标题设计
标题格式三种,看结构选,不是统一用一种。
**冒号格式**(主题:补充说明)适合:术语首次出现需要解释(ACI:工具是给 Agent 的交互界面)、主题加原则性结论(安全沙箱:边界比功能重要)。
**一句话格式**适合:副标题只是弱标注、没什么信息量;两部分读起来断裂感强;副标题和主标题意思重复。
NO: 幻觉放大:多 Agent 特有的坑("特有的坑"是弱标注,删掉信息量不变)
OK: 多 Agent 下幻觉会互相放大
NO: 长程任务:跨上下文的工程问题("跨上下文"和"长程任务"意思重复)
OK: 长程任务的跨上下文挑战
**标题里保留逗号**:先...再...顺序句(先修复评测,再修复 Agent);对仗枚举(同步决策,异步 I/O)。
**标题里逗号改冒号**:"主题,副标题说明"这种格式,逗号换冒号更清晰(Prompt Caching,降低重复调用成本 → Prompt Caching:降低重复调用成本)。
**标题里去冗余词**:「应该如何X」去掉「应该」,直接用「如何X」;副标题如果和主标题意思重叠,合并成一句。
NO: 「Agent 评测应该如何做」 / 「工程实现应该遵循什么顺序」
OK: 「Agent 评测如何做」 / 「工程实现遵循什么顺序」
**标题不要做成名词并列**: 三个名词用顿号串起来,没有观点,读者不知道这节想说什么
NO: 「蒸馏、专用化与持续迭代」 / 「数据、算法与系统」
OK: 用一句有判断的陈述:「前沿模型发布后,训练链路还在继续跑」
判断标准:标题能不能单独成立为一个有意义的句子,能就留,不能就改
**标题要有判断,不只是名词**:好标题表明立场或结论,不只是列出主题词。
NO: 「多 Agent 协作」(只说了主题)
OK: 「多 Agent 协作让幻觉互相放大」(有判断)
OK: 「评测先于 Agent,不然你不知道在修什么」(有立场)
判断式标题适合分析 / 论证型文章;叙事、经历、探索型长文里,疑问句和好奇式短语往往更抓人(「机器人怎么知道自己在哪」「一根没有销钉的手指」),同一篇里两种可以混用。作者已经定的标题形式优先,别为了「凑判断」改掉一个好的好奇式标题。
> 逐字符标点(全角 / 半角、破折号、中西空格)由门禁脚本 `check-punctuation.sh` 检测;下面几节只保留「什么时候该破例、引号何时该用」这类判断,不再逐条机械列举。
### 引号("")
引号不是强调键。不要因为某个词"感觉重要"就套上引号。
同理,`「」` 也不要拿来做视觉强调。
NO: 大模型最擅长"翻译"
NO: 防止 Agent 通过改标准来"提升"分数
NO: Agent 扫了一眼,觉得"差不多了"
OK: 大模型最擅长按规格说明书做对照实现
OK: 防止 Agent 通过改标准来刷分
OK: Agent 扫了一眼觉得差不多,直接宣告完成
引号只用于:直接引述他人说的话、系统输出内容、错误消息。
### 括号()
括号里的内容超过 10 个字,多半说明它该待在正文里,而不是括号里。
同一段里出现 2 处以上括号时,优先把至少 1 处改写进正文,避免读感变重。
NO: 传统 APM(Datadog、New Relic 这类监控延迟和错误率的工具)基本帮不上忙
OK: 这类只监控延迟和错误率的传统 APM 基本帮不上忙
NO: 并发的正确用法不是多模型推理,需要并发的是(文件操作、网络请求、长耗时命令)
OK: 需要并发的是文件操作、网络请求、长耗时命令,模型推理本身保持单线程
可以保留括号的情况:术语首次出现的英文原词、简短数值范围(5~15%)、代码参数说明。
### 分号(;)
分号不是懒人句号,生成的叙事里默认不出现分号(把多个 bold 段落并成一段 prose 时串项的分号除外)。两个放在一起就是因为"感觉有关系"的句子,多半应该直接用句号断开。
NO: File System State 开销只有 5~15%,几乎总是值得的;Verifier Agent 能提升 25~40% 精度,但成本增加 1.5~3 倍
OK: File System State 开销只有 5~15%,几乎总是值得的。Verifier Agent 能提升 25~40% 精度,但成本增加 1.5~3 倍
可以保留分号的情况:
- 对仗句:只跑 Layer 2,评分标准会漂移;只靠 Layer 1,根本看不过来
- 列表项分隔:本地 Shell 能处理的;只需静态知识的;还没验证过的
- 中文枚举:一是…;二是…
分号后面接转折词是典型错误,直接断句:
> NO: `...也决定后训练有没有可以利用的空间;但它没有决定这个模型会不会听指令`
> OK: `...也决定后训练有没有可以利用的空间。但它不决定模型会不会听指令`
### AI 味检测:这几个模式帮你认味
以下是高频出现的具体模式(举例,不完全),用来识别 AI 味,不是必须逐条执行的清单。句子本来自然、清楚就别套规则,命中了味再改。
**1. 升华句**: 没有作者依据、套在任何文章都成立的拔高,不包括作者借经历表达的真实感受
> NO: "很多东西都是这样,当初成立的假设,过一段时间回头看可能已经不成立了。"
> OK: 直接给出具体建议,不升华
> 结尾自查:区分重复解释、凭空拔高和作者自己的感受。前两类可以精简,后一类保留,即使删掉它不影响事实也不能据此判为冗余。「更短、更克制」不是改好的证明;拿不准是不是作者原话,先查原稿或保留。
**2. 翻案腔**: 先给读者立一个他并没有的误解,再推翻它给下文抬价。禁的是这个动作,不是字面,换一套字继续做同一件事仍然算。常见外衣:字面翻案(不是 A 而是 B / 并非 A 而是 B / 不在于 A 而在于 B)、省字与跨句变形(不是 A,是 B / 不是 A。而是 B)、以为体(你以为 A 其实 B / 我一直以为 A,后来才发现 B / 回头才发现)、让转体(看似 A 实则 B / 直觉上 A 但实际上 B / 答案恰恰相反)、抬价体(A 不重要,重要的是 B / X 本身没有价值,真正有用的是 Y / X 已经不是瓶颈,Y 才是)、与其说体。
> NO: "代码生成不再是瓶颈,评审才是"
> OK: "代码生成不卡人了,真正费力气的是评审"
> 改法:找到这句原本想下的判断,从正面把它说出来,依据放在旁边。不要换一种漂亮句式替换。
> 文章确实用材料走过了从误解到修正的过程时,保留自我修正和作者的说法,不限次数。要改的是没有材料的反转,或换了词仍在重复同一个判断的句子;技术上的对照、适用条件和风险边界不能跟着删。
> "不只 A 还 B" 是正常中文的递进,可以用;它出现在翻案位置、用来给下文抬价时,按翻案腔处理。
**3. 重复核心观点**: 同一个道理在不同节出现 2-3 次
> NO: 第 0 节说了,第 2 节总结处再说一遍,结语再升华一遍
> OK: 说一次,后面直接引用或跳过
**3b. 段内重复**: 同一段里把同一个意思用不同措辞说两遍
> NO: "没有约束边界的 Agent 不是更自由,而是更容易在错误路径上跑很远都没人察觉。加约束,是给它一个出了错还能拉回来的余地,没有约束的 Agent 跑偏了都没人察觉。"(后半句和前半句说的是同一件事)
> OK: 说一遍,够了。不需要用换一种说法再强调一遍
**4. 引用+解释句式**: 先抛英文术语/名言,再解释
> NO: "工程界有句话 'X',对 Y 同样如此,..."
> OK: 直接用口语解释:他们把这个叫 progressive disclosure,就是不要一下全给
**5. 工整并列总结**: 每节结尾必有一句"三层缺一不可"型总结
> NO: "三层缺一不可,只靠 A 没用,只靠 B 不够,单独拿出来都有漏洞。"
> OK: 口语化:用下来感觉,少任何一层都会出问题
**6. 瓶颈转移句**: 一眼 AI 博客
> NO: "瓶颈从'X'转移到了'Y'"
> OK: 直接说新情况:代码生成不卡人了,压力全压到评审这一侧,不用"瓶颈"这个词
**7. 优先级结论句**: 听起来像教条
> NO: "安全边界要比任何功能都先做好" / "是生产级别必须的"
> OK: 说清楚为什么:能跑 shell 就能删库,安全得先来
**8. 系统性定义开头**: 教科书格式
> NO: "X 是对 Y 的系统性评估,包括测试用例、评分标准和自动化验证流水线。"
> OK: 从问题或场景出发,不从定义出发
**9. 同构排比**: 同一个引导词或同一副句型连排三次以上,整齐本身就是模板信号。标题、bold 小标题和句子内部一视同仁。
> NO: 四个 bold 标题全是"有明确的参照物,让 Agent 翻译而不是创造"这种格式
> NO: "为什么出发,为什么放弃,爱过什么,怕过什么"(四项同构,克制得很整齐)
> OK: 两项为限,第三项换一种说法或者删掉。标题让每个点语气不同,或直接改成散文叙述
**10. 顺序/重要性结论**: AI 喜欢用"所以"收尾说教
> NO: "所以顺序很重要:评估工具可信,测量结果才有意义。"
> OK: 直接说怎么做:"怀疑 Agent 出了问题,先确认 Eval 没问题。"
**11. 混杂段落**: 一段塞了多个不相关话题
> NO: 一段里同时讲可观测性顺序、File System State 成本、Verifier Agent 精度、多 Agent 引入时机
> OK: 每段只说一件事,其余拆到各自对应位置,没有对应位置的直接删掉
**12. 章节引介过渡句**: 章节结尾或开头写一句引出/承接下一节的桥接句
> NO: 「上面这些模式解决的是控制流怎么搭,下面再看另一个更工程的问题,系统为什么能跑稳。」
> NO: 「X 保证了方向对了,执行完还得确认它真的做对了。」← "X 做完了,还有 Y"的机械衔接,只在宣告结构,没有信息量
> OK: 直接删掉,章节标题本身已经承接
**例外:这几类章节引导是必要的,不要删:**
- **内容说明**:告诉读者这节为什么值得看,有实质信息量
> OK: 「这块很少有教程展开讲,但它很影响成本结构和设计取舍。」
- **模式切换信号**:长文里从原则转向案例、从理论转向实操,读者需要知道内容性质变了
> OK: 「前面几节讲的是原则,这一节直接看一个真实项目怎么落地。」
- **行动指引**:结尾给读者一个具体的下一步,不是在说文章做了什么
> OK: 「想知道自己的配置离这些原则差多远,跑一次 /health 是最快的方式。」
判断标准:句子里有没有对读者有用的信息,有就留,只在宣告文章结构就删。
**13. 收尾"给你答疑/给你答案"**: 文末或节末用「应该能给你答疑」收尾
> NO: 「下面文章应该能给你这些问题答疑。」
> OK: 省略,或改成:「读完这篇,这几个问题应该能有些答案。」
**例外:图后说明段落不能以"重复"为由删除**
图片的 alt text 读者看不到,图后的文字说明才是读者真正看到的解释。图后说明段不属于"收尾总结",不要删。
判断标准:如果删掉这段文字,读者就看不懂这张图,就必须保留。
**14. 段末收尾总结句**: 一段刚解释完某个机制,结尾再加一句把刚才的意思重述一遍
> NO: "这不只是设计选项,是资源约束的结果": 紧接在已经解释了具体代价之后
> NO: "模型本身成了训练流水线里的工具": 刚解释完模型在给下一代产出训练数据
> NO: "主流实验室基本都在用这类多阶段配方了": 四个阶段刚刚描述完
> OK: 说完就停,事实本身已经说明一切,不需要再"总结"一遍
> 识别信号:这类句子往往以"到这里"、"这说明"、"这本身就是"、"也就是说"、"可以看出"开头,或者是段落最后一句比其他句短且抽象
> 句子级同理:动作、数字、原话和比喻已经把判断或情绪写出来时,不要紧跟一句替读者解释。留白只省读者自己能得到的那一步,关键事实、因果和取舍仍然要说清
**15. 英文术语首次出现后,后续一律用中文**
- 首次出现:保留英文 + 中文注解,或中文前置 + 英文括注(`指令微调(Instruction tuning)`)
- 后续出现:只用中文,不要英文/中文混用
> NO: 第一次"冷启动 SFT(cold start)",第五次还在写"cold start 让 RL 稳定启动"
> OK: 第一次解释清楚,后面一律写"冷启动"
- 例外:SFT、RL、MoE 这类已成行业通用缩写,可以全程保留缩写形式
**16. 思考过程用了物理动作动词(翻译腔套路一)**
接住、击穿、锋利、不崩、不爆、打穿、扛住、收紧、落地、推开、撑不住,这类词把抽象认知过程想象成了物理动作,骨架是英文(catch / pierce / sharp / break / blow up)。
> NO: 「你这几条我都接住了。」「这个论证被击穿了。」「更锋利的重构:」
> OK: 「你这几条我都收到了。」「这个假设不成立。」「换一种更准的讲法:」
自检方式:写完一段,把所有动词圈出来,凡是中文日常不会这样用的,挨个换掉。
**17. 形容词预判+冒号引出内容(翻译腔套路二)**
「更干净:」「逻辑很清晰:」「问题很直接:」「结论很明确:」,形容词抢先下判断,冒号才引出事实。「结果:」「原因:」这类光杆名词起句同属此类,删掉引出词直接说。
问题有三:抢走了读者自己评估的机会;形容词几乎总是多余的;后面的事实本身就能让读者得出那个感受。
> NO: 「验证得比我预期的更干净:上下文不崩、成本不爆、状态可恢复。」
> OK: 「数据把你的感觉证出来了:上下文不乱、成本压得住、状态能恢复。」
改法:直接把形容词那一节删掉,只留后半句事实。如果非常想保留那个形容词,多半说明后面内容没讲清楚,要回头补内容。
**18. 抽象名词做主语,形容词当结论(翻译腔套路三)**
"X 的 Y 比 Z 更 W"这种骨架,主语是抽象名词(工程上的现实、这份数据、生态的成熟度),结论是形容词(难看、直接、漫长),中间系动词连接。读完不知道到底哪儿难看、难看在哪个环节。
> NO: 「工程上的现实比这些数字难看。」
> OK: 「这些数字只反映了采用面;真往下看各家怎么接,早就对不齐了。」
改法:让人、动作、或具体对象做主语,让事实自己说话。凡是碰到"X 的 Y 比 Z 更 W"骨架,都应该重写。
**19. 有稳定中文对译的英文词直接混入(翻译腔套路四)**
context、state、cache、claim 这类词有现成中文对译:上下文、状态、缓存、断言。留着英文原词让读者每次切换都消耗注意力。
> NO: 「context 不崩、成本不爆、state 可恢复、cache 命中率高。」
> OK: 「上下文不会乱、成本压得住、状态能恢复、缓存命中得上。」
判断标准:中文技术圈已有通用译法,就换中文;术语在中文圈还未收敛(如 prompt、embedding、tokenizer),保留英文合理。缩写例外同第 15 条。
常见可替换词:context→上下文、state→状态、cache→缓存、claim→断言、runtime→运行时、contract→契约。
**20. 挑战与未来展望模板段**: "尽管面临挑战,X 凭借 Y 仍将持续蓬勃发展,成为 Z 不可或缺的一部分。" 是 AI 最爱的收尾套路。
> NO: "尽管面临挑战,X 凭借 Y 仍将持续蓬勃发展,成为 Z 不可或缺的一部分。"
> OK: 直接说具体的挑战和具体的应对,不要"尽管…仍…"+"未来展望"的套路收尾。
**21. 同义词循环**: 同一段里把同一对象用 3 种以上同义词替换,是 AI 重复惩罚机制的副产物。
> NO: "主人公面临挑战。主角必须克服困难。中心人物最终获胜。英雄回到家中。"
> OK: 选一个称呼用到底。"主人公面临挑战,最终获胜回到家中。"
**22. 虚假范围**: "从 X 到 Y" 结构里 X 和 Y 不在同一尺度上,假装覆盖全光谱。
> NO: "从大爆炸的奇点到暗物质的神秘舞蹈"(不是同一类对象)
> NO: "从架构设计到团队文化"(跨域强凑)
> OK: 直接列出真正涵盖的两三件具体事,或者只挑一件展开。
**23. 知识截止日期免责声明残留**: AI 关于"截至 X 时间 / 根据可用信息 / 现成资料中没有广泛记录"的免责声明被原样粘贴到正文。
> NO: "虽然关于公司成立的具体细节在现成资料中没有广泛记录,但它似乎是在 90 年代的某个时候成立的。"
> OK: 要么查清楚直接写"成立于 1994 年",要么不要写这一句。模糊免责声明本身就是 AI 痕迹,要删干净。
> 注: 与「时效性事实加边界词」不同,那条是作者主动加边界("截至 2026-04 我看到的是…");这条是 AI 自动生成的甩锅式免责。
**24. 长定语堆在名词前面(翻译腔套路五)**: 英语的从句可以挂在名词后面,中文的定语只能堆在前面。把英语式层层嵌套搬进"的"字之前,读者要先扛完一大串限定,才知道谁在做什么。
> NO: "在经历了长达数月且始终没有稳定产出的反复调参之后,最终促使团队换掉这套方案的,是一次线上超时。"
> OK: "这套方案调了好几个月,产出一直不稳。后来线上超时了一次,团队才把它换掉。"
> 自检:一句话里出现三个以上的"的",或者读到名词时已经忘了句首,就先找真正做事的人,把他和动作放到最前面,原因、时间、条件和例子接到后面。圈出"……的,是……""更……的是""真正让……的""那些……的……"这几副骨架逐个重写。不要机械切短,复杂关系允许用长句讲完,只要主干早点出现
> 配套的是顺势接话:让后一句接住前一句刚出现的人、物或结果,指向清楚时删掉重复的主语和机械连接词,句子自己会往前走。两个人同时在场、前一句出现多个名词、或者中间插了一大段话时,把名字写回来,自然不等于含糊
**25. 格言化收束**: 段末把刚说的事压成一句可摘抄的警句,常见外衣是格言句、对仗句、工艺感叠词("一X一X地")。单看漂亮,问题就在漂亮:是修辞表演,不是作者说话。
> NO: "它能提效,却很难提升彼此的感情和信任,这些还是得一场对话一场对话地攒。"
> OK: 停在事实上,或用作者素材里的原话收
> 自检:段末一句能做成金句卡片的,删掉或改平。
**26. 给读者贴标签**: 替读者定义身份("普通人""小白")或预设心理("买不买")。描述再准,被归类的读者也不舒服,标题里最致命。作者素材里的自称不算。
> NO: "教会我怎么给普通人做产品"
> OK: 用"大家""你",或直接说事情本身
**因果/结论语气要像归纳,不像定论**
技术文章里,判断方向没问题,但懂行读者看到”换来的是/就是从这里来的/是X的路径”这种语气,容易挑刺。改成归纳语气:
> NO: “换来的是同等参数下更高的能力密度” / “性能差距就是从这里来的” / “也是能力解耦的路径”
> OK: “公开结果显示,这类配方通常能换来更高的能力密度” / “从公开结果来看,差距通常是可观测的” / “一个关键原因是能力解耦”
反过来也有度。边界说明一次就够,每句都挂"可能""或许""不能排除",作者会从文章里消失。材料支持七成就说到七成,剩下三成直接承认不知道,通常比再补五个限定词更可信。
**术语列表需要”为什么值得记”的锚**
一段里列出多个专有名词(muP、WSD lr、batch size 等)而不解释为什么读者该记住,更像点名单不像论证。加一句:
> NO: “这些都开始出现在正式训练报告里。能看出来的一条线是,配方越来越细。”(只说存在)
> OK: “这些细节之所以值得记,是因为它们正在成为同规模模型之间拉开差距的地方。”(说明为什么重要)
做法/规矩清单同理:连列几条实现规则而不写读者会看到什么,读者只觉得"不知道讲了啥"。每条补上它挡住的可见现象,结尾一句说清合起来是什么。
**长段落拆分标准**
一段超过 400 字、或塞了 3 个以上独立概念,移动端读起来会累,也容易冲淡最重要的那一句。拆分点:概念切换处加空行,不要因为”都属于同一主题”就塞在一起。
**案例引入需要半句”为什么选它”**
直接 zoom in 到具体案例时,如果切换感突然,先加半句说明它代表哪条路线或设计选择,再展开细节。
> NO: “X 是这里最值得拆开的工程案例。它只训练 orchestrator...”(直接进细节)
> OK: “X 代表了一条新路线:只训 orchestrator、把 credit assignment 收到编排层。奖励信号分三类...”(先说代表什么)
### 工程技术文实战规则
- 删段之前先确认信息量。删除整段(不是单句)之前,确认这段没有在别处重复的技术事实、数据或逻辑。”删掉段末总结句”的规则针对结构性重复,不适用于展开新信息的段落。如果不确定,改成提议而不是直接删。
- 括号和引号默认少用,但术语首次出现、歧义消解时要保留,不要为删符号牺牲清晰度
- 避免抽象判词堆叠,优先写可观察事实、链路位置和具体影响
- 保留作者的内在矛盾。如果原文在不同段落持有看似矛盾的立场(比如既推崇自由又强调纪律),不要替作者消解。时间性矛盾(早期 vs 近期观点)标注演化即可;领域性矛盾(工作 vs 生活的不同规则)保留两面;本质性张力(价值观的内在冲突)原样呈现,这通常是文章最有深度的部分
### Summary 写法
`summary` 先判断目标,不同目标写法不同:
- 导读型:告诉读者“这篇会讲什么”,不提前讲完整结论
- 总结型:概括“这篇讲了什么 + 得出什么结论”
实操规则:
- 导读型用“这篇会从 A 讲到 B,重点看 C”
- 总结型用“这篇从 A 到 B,最后看到 C”
- 不要混写,避免一段里既像导读又像结论宣判
- 默认 2 到 3 句,信息完整但不铺满正文细节
### 高频替换:报告腔和不喜欢的表达
按「左边少用,右边推荐」执行,读起来有同样报告腔或不自然的一律适用。
| NO | OK |
|----|----|
| 主要叙事 | 大家通常用...来解释... |
| 系统梳理 | 从...一路讲到... |
| 核心结论是 | 最后会看到 |
| 逻辑分工 / 流程节点 | 为了看分工 / 更细的版本 |
| 公开口径里 | 按公开数据 |
| 共同模式已经很稳定 | 能看到几个共同点 |
| 可以从三个角度看 | 可以先看三件事 |
| 不需要读成 X | 不只是 X |
| 别掉链子 / 露过头(过重口语)| 直接说事实 |
| 团队 / 实验室 / 行业(抽象主语)| 能写动作就别先摆抽象主体 |
| 真拆开看 / 这背后是同一个变化 / 真正的问题变成了 / 真正关键的问题是 / 都在说明同一件事(讲解腔起手)| 直接说 |
| 硬约束(工程黑话)| 约束 |
| 摆出来(半口语半讲稿)| 直接陈述 |
| 当然 / 这话不算错,但 / 这么理解不能说错,但(让步腔)| 直接说结论,不先让步再转折 |
| 塑形 / 塑造(行为、方向、结果)| 调整 / 决定 / 影响 / 优化 |
| 相邻(空间词描述逻辑关系)| 关系紧密 / 有交集 |
| 聚焦(空泛目标词)| 重点写什么 / 主要看什么 |
补充:比较表达优先自然中文,`最清晰` 通常比 `最清楚` 更稳;避免「句子评价句」,「这句话解释不了」直接写成「X 解释不了 Y」。
### 参考资料排列
- 默认用单列表,不强制按“论文/博客/仓库”分类
- 排序按承重程度,不按发表时间
- 论文和官方技术报告优先,仓库与博客只保留高承重项
- 数量以可读为先,通常 10 到 14 条比较稳
### 文章自说明句(默认少写)
文章不该反复谈自己在做什么。默认直接进内容,必要时只保留 1 句路线提示。
NO: `先给最短结论,就四句:`
NO: `这篇真正想钉住的,就是这件事:`
NO: `如果先只记一张表,可以先记住这六层:`
NO: `脑子里还是那老三样:X、Y、Z`(代替读者做归纳)
NO: `最常见的解释就是三件事:`(同上)
NO: `拿一组具体数字看会更直观。`(宣告数据要来了)
NO: `如果说...那...`(条件句引入段落)
NO: `这篇文章接下来会从 X 讲到 Y。`(同上)
OK: 直接进结论、进数字、进内容。结构交给标题和逻辑顺序,不要靠过渡句反复宣告。
OK: 如果确实需要路线提示,只写一遍,避免在开头和结尾重复。
### 术语注解不要扎堆
多个术语同时首次出现时,括号注解会堆在一起,读起来很重。处理方式:
- 把注解融入行文:`GRPO(组相对策略优化),而不是传统 PPO(近端策略优化)` → `这里的关键选择是 GRPO 而不是传统的 PPO`,注解分散到后文
- 把首次介绍提前:在自然引出术语的地方加注,而不是在密集使用的地方一次性堆上去
- 如果上下文已经说清楚了意思,括号注解可以完全省略
### 图片引导语
图片前的说明句不用「下图」,用「这张图」或「下面这张图」,避免每句都一样。
### 图片节奏
带图技术长文里,图片放哪儿决定整段读起来顺不顺。三条经验:
**图当 TL;DR,列表/表格当详情**。图片放在「intro 一句话 + 图 + 列表」这个位置最舒服。把图丢在列表/表格末尾、紧贴下一个 `---` 的位置看着空,上面是序号 + 加粗也很丑。
**图片 alt 要和正文 prose 对齐**。如果 alt 列了 6 项保号清单、prose 改成 5 项,要么改 alt、要么重画图,不能让两者错位。读者扫到的是图视觉 + 正文文字,alt 给搜索引擎和无障碍。
**段末"为了平衡而加图"是 AI 模板**。图不是装饰节奏的工具。如果某段没自然想到画什么,就别凑图,文字密就让它密。
### 技术文章额外规则
**事实与可验证性优先于去 AI 味**
技术文章先保准确、清楚、可验证,再追求自然。不要为了删 AI 味,把版本、环境、依赖、出处、复现条件、风险边界删掉。如果原文缺这些信息,不要编,可以保留问题或收敛语气。
- 版本、平台、依赖会影响结论时,要保留或补全对应上下文
- 引用“官方建议”“文档说明”“测试表明”时,要有具体出处;没有出处就不要写成定论
- 命令、配置、代码不要只贴结果,要解释关键参数、关键路径和为什么这么改
- 性能、安全、稳定性结论要说明观察口径,比如数据来源、测试环境、样本范围
- 个人经验、单次排障、局部实验不能写成通用规律
- 写方案时顺手补一句边界:什么时候适用,什么时候别这么用
**时效性事实加边界词**
价格、订阅档位、版本号、产品名、隐藏命令、发布时间这类容易过时的事实,加边界词写:「截至 2026-04」、「我这里看到的是」、「以当时显示为准」、「目前」。读者过两个月对着失效信息照搬时,能立刻看到这是哪一时刻的截图。
**专有名词改前 verify**
字体名、品牌名、书名、人名、产品名改之前先 grep 或查官网,不凭印象,同音异字最容易改错。事实类改动比风格类改动风险高,反而最容易拍脑袋下手。
**开头不要放背景铺垫段落在最前**,比如"国内现在也有不少打着 X 旗号的产品..."这种定性背景,放到正文具体位置更自然,不要作为全文开场白。
**技术术语解释不用分号并列**
> NO: "系统提示的三层结构:身份定义,常驻、轻量;约束规则,常驻、精确;领域知识,按需加载。"
> OK: 直接说:"系统提示分三层:身份和约束规则常驻,领域知识按需加载。"
**场景描述代替核心问题格式**
> NO: "这类系统要解决的核心问题是:Agent 常驻服务器,同时需要..."
> OK: "场景是:Agent 常驻服务器,要同时接多个渠道的消息..."
**数据后不要加 AI 营销腔结论**
> NO: "token 节省 37%,消除 19+ 次无效推理。"
> OK: "token 从 15 万降到 2000,中间 19+ 次来回推理也省掉了。"
**幻觉/错误原因不用学术解释框架**
> NO: "原因在于 LLM 的中立性偏向。以'帮助、诚实、无害'为目标训练出来的模型,在群体讨论中天然倾向服从..."
> OK: 直接说现象:"LLM 训练目标是'帮助、诚实、无害',在群体里就会倾向不反对多数人。一个说错了,后面的顺着走。"
**不要用外来术语/模型名做比喻,除非读者肯定知道**
> NO: "这就是瑞士奶酪模型的现实:每一层都有漏洞"
> OK: "每一层都有盲区,叠在一起才够用"
西方管理学模型名(瑞士奶酪模型、长尾定律等)、外语质量管理术语(Poka-yoke)在中文技术文章里多数人看不懂,去掉名字,直接说清楚那个道理就行。
**多方案例并列时合并叙述,不要各自单独成段**
同一个技术观察涉及 3 家公司/方案时,合并成一段,每家贡献一句话,读起来像分析而不是列表。
> NO: 三段分别讲 Kimi、Cursor、Chroma 的做法,每段独立成段
> OK: "三家承担的重点也很清楚。Kimi 用 PARL 解决...,Cursor 用 self-summarization...,Chroma 则把 prune_chunks 训成策略本身..."
**有名字的来源自然融入句子,不要做括号注释或单独引用句**
> NO: (根据某某的分析)今天 agentic model 报告共同模式已经很稳定
> NO: 根据某某的研究,今天 agentic model 报告共同模式已经很稳定。
> OK: "把某某对这三家方案的归纳放在一起看,能看到几个共同点。"
### Polish 后期残留 checklist
风格级 AI 味清干净后,还有一类「句子级残留」最容易被漏掉。每轮 polish 末尾按这 6 类扫一遍:
**1. 叠词拗口**:同一句里出现两个意思接近的词("也能上手用"、"也都"、"也是另外")。识别:把句子拆成最小动作主谓宾,看哪个修饰是冗余的。
**2. 缺逗号长句**:超过 25 字的句子里没有逗号断点,多半在某个时间状语 / 条件状语后漏了「,」。识别:朗读时想换气的位置就是该加逗号的位置。
**3. 中英术语一致性**:同一篇里同一个常用词(copy / 复制、context / 上下文、setting / 设置)反复切换形态。识别:搜文中所有英文 token,看是否有对应中文已成主流,主流就统一为中文。
**4. 借来的比喻**:两类。一是作者脑子里有的比喻("工作台"、"水位"、"账本")没在前文铺垫就直接用,第一次出现的非通用比喻词,前一段必须有铺垫,没有就换通用词。二是拿实物给抽象概念穿衣服,仓库、抽屉、温度、坍塌、浪潮、钥匙、底座是高频几件,拿比喻词当文章骨架的主旋律、主战场、这条线、热起来、收官同属此类;同一类还有给抽象名词配具体动词的抒情,时间保管细节、焦虑显出形状、岁月磨平什么。写的对象本来就是这些东西时不受影响,不是就先删掉,删掉意思没损失就不要再换一个新比喻。短距离内借来三套不同的世界(道路、战争、建筑、温度),全部还原成本义。
**5. 「上 / 下 / 里」字残留**:「方案上都」「思路里」「概念上」这类介词残留是早期草稿的痕迹,删掉不影响意思。识别:搜「上都」「里都」「上的时候」。
**6. 缺关键修饰**:某个名词第一次出现需要修饰才能区分性质("某公司官方推出的 X" vs 光杆的 "X"),漏了读者会和上下文混淆。识别:每个产品 / 工具名第一次出现,问一句"读者能否区分这是谁家的、跟前文工具的关系"。
判断标准:1-3 类是字符级;4-6 类要带上下文判断、需要全文回读。
### 专家腔
润色容易把作者改得比原文更像专家、更会教人。下面这几处是同一个倾向的常见表现。
**0. 实作经历不能为了工程感补细节**
> NO: 作者只说"串口通信难",润色时补成"ACK 丢了、超时重传、翻串口日志定位"这类原文没有的排障故事
> NO: 同样地,原文只说"串口通信难",不要替它补出"波特率、指令格式、模块状态"等原文没提的具体参数
> OK: 只顺句、不加事实,原文给什么写什么:「串口通信我反复试了好多次才弄对,差一点就放弃了」
> 判断:硬件型号、协议字段、波特率/指令格式等参数、日志结论、故障原因、排查顺序,只要原文或用户没给,就不要补。原文若已写明这些参数,可以保留并顺句,但不要再往上叠新的故障细节。技术文的人味来自真实细节,不来自替作者编一个更像工程师的 Debug 故事。
> 通用形式是假具体:精确时间、天气、神态、桌上的冷咖啡、第三根烟、突然出现的原话,逐项问它来自哪里、会不会改变后文。没有来路也没有作用就是装饰,删掉。假细节越具体,AI 味越重。真正有用的是知识的来路,作者在哪里知道这件事,起初哪里想错了,哪条材料改变了判断,哪一块到现在还拿不准。这几项每篇挑一两处自然出现,凑齐了就成了新模板。
**1. 元叙事 in 正文**(跟"文章自说明句"是 PRE-meta,这条是 IN-BODY meta about article-writing itself)
> NO: 「写到这里」「写文章时」「更稳的写法是」「我把 X 单独拿出来讲」「这里要小心一个说法」放在正文里
> OK: 直接陈述,或换成「一圈下来」「更准确的说法是」这种不带"写"字的衔接
> 例外:末尾 italic publishing note 可以用「本文完成于 X」这种 convention
**2. 二人称读者预测**(跟"训人感起手"是命令式,这条是预言式)
> NO: 「你会自然地问」「你会知道 X」「你会同时多出 X」「你会发现」
> OK: 去"你"做主语,事实陈述:「翻论文时会先看 X」「同时会多出 X」「自然会想问 X」
**3. 段首 announce-a-feature 引子**(rule 12 章节引介过渡句的段内变体)
> NO: 「X 也是容易被低估的部分:」「X 也是 Y 的延伸:」「还有一项 Y 经常被低估:」「我了解到 X 常见的 Y 有这几类,工程代价也各不一样:」
> OK: 直接进 X:「X 也容易被低估:[直接讲]」「再往后一层是 X:[直接讲]」,分类引子直接进列表或表格
**4. 章节编号一/二/三 prefix**
> NO: `## 一、X` `## 二、Y` 中文数字编号像教材目录
> OK: 去前缀,标题留描述/判断
> 例外:参考文献、操作步骤、明确有顺序的列表可保留 1./2./3.
**5. 形容词 announce-claim 不带列表**(扩 rule 17,rule 17 收的是 `形容词:[列表]` 这种)
> NO: 「原因很朴素」「X 很现实」「代表很现实的优势」「展示了很强的运动性能」(中间陈述,不是列表前)
> OK: 删形容词,直接给事实:「原因很简单:[事实]」「H1 运动性能也很强」「国内挺实在的两个优势」
**6. "X 在于 Y / 价值在于 Z" 名词化 claim 公式**(跟 rule 18 抽象名词主语相近,这条是"X 在于 Y"系动词结构)
> NO: 「减少 X 损耗」「价值在于 Y」「吸引力是 Z」「区别在于 W」「关键在于 V」
> OK: 动词化 + 主语具体化:「省掉 X 的麻烦」「最大的好处是 Y」「跟 W 不一样的是…」
**7. bullet 跑成"纯记录" → 第一人称叙事,但只用已给细节**(扩列表去 list 化策略)
> NO: bullet 全是 passive 陈述(「X 是 Y / X 烧过 / X 不够硬 / X 必须自己写」),读起来像故障报告
> OK: 转 prose 段,优先用原文已有的动作和状态:「踩坑 / 老老实实 / 晃 / 不太对劲 / 最难的是 / 我」
> 例:bullet「OLED 带电插拔烧过一次,后来所有接线都先断电」→ prose「OLED 我带电插拔烧过一次,之后所有接线都老老实实先断电」
> 风险:不要为了让段落更"工程师"而补协议、日志、ACK、重传、供电、接触不良等未经确认的排障细节。
**8. 紧邻 2 段 setup→continuation 用逗号 fuse 成一段**(跨段落版的"句号密度要控")
> NO: 紧邻两段,前段铺垫、后段承接,中间用段落 break + 句号
> OK: 去段落 break,改逗号融合
> 例:「它都不知道。它跑通的是…」→「它都不知道,它跑通的是…」
> 判断:后段是 elaboration、example、follow-up 时合;topic 切换时不合
**9. 第一人称 anchor 的边界**
> 实作经历、开头动机、踩坑过程可以保留「我」;技术判断、产业分析、表格列名和结论段尽量让事实做主语。
> NO: 一篇 7 章长文里反复出现「我会先看」「我更在意」「我盯」「我不会把」「我自己看一家公司」
> OK: 「动作空间、控制频率和真机测试比参数量更早决定可比性」「公司层面的差异,最后会变成几个问题」
> 判断:第一人称是经历证据,不是审稿姿态。中后段如果像作者站出来教读者怎么看,优先删主语或改成可观察事实。
**10. 学习路径不要改成入门教程**
> NO: 「入门不用一上来」「先把 X 跑通」「再往前一步」「几个常见坑」「这一圈下来,我先把收获留成检查项」
> OK: 「按我现在的理解,资料大概能这样串起来」「跑过一遍后,哪些词会落到具体字段上」「还有几个问题我现在没答案」
> 判断:原文如果是工程师边做边学、把材料慢慢串起来的过程,不要改成老师给读者安排课程。学习路径可以写,但语气要像作者自己的探索路线。除非用户明确要教程,少用命令、清单、检查项、常见坑、入门教程这类框架词。
**11. 不要把轻一点的作者口气修成老派说教**
> NO: 「盯」「检查项」「安全动作放前面」「失败样本要不要留」「更有抓手」「后续继续跟踪」「需要重点关注」
> OK: 「看」「几个判断更具体」「限角度、限速度、急停这些东西要留在本地」「失败数据怎么处理」「会落到更具体的问题上」「我现在还没有答案」
> 判断:这些词本身未必错,但在博客里容易变成老派专家指导读者。作者原本如果是「我跑过 / 我看到 / 我还没答案」的探索口气,不要润成「我来告诉你该怎么做」。
#### 工程文用词替换补充
| NO | OK |
|----|----|
| 损耗(信息)| 麻烦 / 信息损失 |
| 落到 X 上 / 落回 X | 就是 X 这些事 / 都看 X |
| 期待 X | 指望 X |
| 提供一层 X | 做一层 X |
| 进一步强调 / 公开资料强调 | 公开资料反复说 / 又把 X 推了一步 |
| 官方曾报告 | 官方说 |
| 收敛到单一路线 | 还没有谁明显跑出来 |
| 把 X 写成 Y(奇怪动词搭配)| X 出在哪里,Y 就… |
| X 也是 Y 的部分 / 延伸 | 再往后一层 / 直接讲 X |
| 写到这里 | 一圈下来 / 整圈走下来 |
| 更稳的写法是 | 更准确的说法是 |
| 学习曲线决定 X | 具体后果(「下一次就不容易再栽」)|
| 阶段性突破点(独立 closer)| 合上一句做因果 |
### 默认禁用(举例,不完全,读起来有同样报告腔的一律适用)
报告腔替换词见上文「高频替换」「用词去正式化」两张表;上文各 pattern 和段落已展开的项,命中即按对应处改,不在这里重列。下面只补上文没单列的高频禁用词:
NO: 本文旨在
NO: emoji(任何场合,包括列表、标题、状态标记)
NO: 这篇是整理出来的结果(冗余,删掉)
NO: 有一个大背景值得先说(直接说)
NO: 读下来有几个地方和直觉不一样,提前说一下(直接列)
NO: 精准命中了 N 个条件
NO: 是最常见的失败模式之一
NO: 拿来即用 / 装好就能用(拿来即用是营销词,自己介绍工具直接说”装好就能用”)
NO: 翻倍 / 翻番(带数字渲染感)
NO: 事半功倍 / 少走弯路(成语腔,前者更重)
NO: 先立住(劝告腔,可改”注意一下”或直接陈述)
NO: 完全没关系(去掉”完全”两字更口语,”没事”更稳)
NO: 把模糊性降到最低(学术抽象,改成”让它要猜的部分变少”这种动词具体)
NO: 最好的 / 最适合 / 直接搞定 / 一句话就出(介绍自己工具的营销词,改成”我自己这么用 / 装好就能用 / 我攒了一套”)
NO: 颗粒度(技术黑话,改成”粒度”或直接说”拆得更细”)
NO: 赋能 / 抓手 / 闭环 / 底层逻辑 / 顶层设计 / 降本增效 / 全链路 / 组合拳(中文商业黑话,改成写清楚谁做了什么、花了多少钱和时间、后果落在谁身上)
NO: 家规(类比腔,直接说”规则”或”约束”)
NO: 黑乎乎(口语过重,改成”偏暗”或”深色”)
NO: 不可或缺(空泛形容,直接说依赖关系或删掉)
NO: 奇妙之处(升华腔,直接说具体现象)
### 对外发文专项检查
公开发文(release notes、推文、博客、newsletter)交出去之前,额外扫三件事:
**1. 身份和敏感信号脱敏**
只处理未经授权向目标读者披露的敏感信息。作者明确用于本文或已经公开的雇主、地点、团队背景和真实经历应保留,不因含有身份线索就删除;披露意图不明确时先确认,不擅自抹去经历,也不补造背景。
**2. 不踩竞品**
介绍自己产品时不主动贬低同类产品。功能不如别人的地方可以不提,不要拿来做反衬。
> NO: "Cursor 那种全局索引很吃资源,我们没走这条路。"
> NO: "不像 Typora,我们做的时候 X 问题想清楚了。"
> OK: "我们的选择是只索引打开的文件,不做全局扫描。"
> OK: 直接说自己做了什么、为什么,竞品不用出现
**3. 用户感受先于功能清单**
release notes 和推文里不要开场就列功能。先给一句场景或感受,再进改动细节。
> NO: "V1.2.0: 新增 X 功能,修复 Y 问题,优化 Z 性能。"
> OK: "改了两处之前用起来总觉得别扭的地方。一是..."
scripts/check_punctuation.py
#!/usr/bin/env python3
"""Check punctuation and CJK/Latin mixing in prose, by locale.
Flags half-width punctuation inside CJK sentences (including across a markdown
emphasis marker, so `**标签**:` is caught), a comma or semicolon glued to the
next Latin letter, half-width parens hugging a Han char, missing or wrong
spacing between CJK and Latin, and em/en dashes. Skips fenced code blocks,
inline code, URLs, and markdown links so punctuation that belongs to code is
never flagged.
Run as: python3 check_punctuation.py [--lang LANG] [--fix] [FILE]
--lang zh | en | ja | auto (default: auto)
--fix print corrected text to stdout (zero-ambiguity fixes only)
FILE file to read; reads stdin when omitted
Default mode prints findings and exits 1 when any exist, otherwise prints
"punctuation: ok" and exits 0.
"""
from __future__ import annotations
import argparse
import re
import sys
# Character-class endpoints (verify against a Unicode table when editing):
# CJK U+4E00-U+9FFF + ext-A U+3400-U+4DBF; KANA U+3040-U+30FF; HANGUL U+AC00-U+D7A3.
CJK = "一-鿿㐀-䶿"
KANA = "-ヿ"
HANGUL = "가-힣"
URL_RE = re.compile(r"https?://(?:\([^()]*\)|[A-Za-z0-9\-._~:/?#\[\]@!$&'*+,;=%])+")
# group(1) captures only the (url) target so the [label] stays visible to checks.
MD_LINK_RE = re.compile(r"\[[^\]]*\](\((?:[^()]|\([^()]*\))*\))")
# Single-class matchers reused across detect_lang / fix_zh_line (compiled once).
_HANGUL_RE = re.compile(f"[{HANGUL}]")
_KANA_RE = re.compile(f"[{KANA}]")
_CJK_RE = re.compile(f"[{CJK}]")
_LATIN_RE = re.compile(r"[A-Za-z0-9]")
_LINE_END_RE = re.compile(r"(\r\n|\r|\n)\Z")
_CJK_KANA_HANGUL_RE = re.compile(f"[{CJK}{KANA}{HANGUL}]")
_LINE_SPLIT_RE = re.compile(r"\r\n|\r|\n")
FULLWIDTH = {",": ",", ".": "。", ";": ";", ":": ":", "!": "!", "?": "?"}
# Half-width keys of FULLWIDTH, reused as the [,.;:!?] character class so the
# map and the regexes built from it stay a single source of truth.
_HALFWIDTH = "".join(FULLWIDTH)
# Full-width CJK point marks: the six values above plus 顿号. A half-width space
# touching any of these is a typesetting error, so --fix strips it; check_en
# reuses the same set as its "full-width punctuation in English" class.
_CJK_PUNCT = "".join(FULLWIDTH.values()) + "、"
# Half-width paren -> full-width, for the CJK-adjacent paren check (report only).
FULLPAREN = {"(": "(", ")": ")"}
def _inline_code_spans(line: str):
"""Yield (start, end) for each inline-code span, CommonMark-style: a run of
n backticks opens and the next run of exactly n backticks closes. A pure
linear scan with no regex backtracking, so a pathological long backtick run
cannot blow up -- and a real short span on the same line as a long run keeps
its exemption (the old regex guard dropped inline-code for the whole line)."""
i, n = 0, len(line)
while i < n:
if line[i] != "`":
i += 1
continue
j = i
while j < n and line[j] == "`":
j += 1
run = j - i
k = j
while k < n:
if line[k] == "`":
end = k
while end < n and line[end] == "`":
end += 1
if end - k == run:
yield (i, end)
i = end
break
k = end
else:
k += 1
else:
i = j # no matching closer; the backticks are literal text
def exempt_mask(line: str) -> list[bool]:
"""Per-character mask; True marks positions inside inline code, a URL, or the
(url) target of a markdown link -- never inspected or rewritten. The [label]
of a markdown link stays visible (it is rendered prose). A trailing ASCII
mark on a bare URL immediately followed by a CJK/kana/Hangul char is released
from the mask: it is a sentence separator, not part of the URL."""
mask = [False] * len(line)
spans: list[tuple[int, int]] = []
for m in MD_LINK_RE.finditer(line):
spans.append((m.start(1), m.end(1)))
for m in URL_RE.finditer(line):
s, e = m.start(), m.end()
while e > s and line[e - 1] in _HALFWIDTH and e < len(line) and _CJK_KANA_HANGUL_RE.match(line[e]):
e -= 1
spans.append((s, e))
spans.extend(_inline_code_spans(line))
for s, e in spans:
for i in range(s, e):
mask[i] = True
return mask
def overlaps_exempt(mask: list[bool], start: int, end: int) -> bool:
return any(mask[start:end])
def split_line_ending(raw: str) -> tuple[str, str]:
r"""Split a raw line into (body, trailing newline) where the newline is one
of \r\n, \r, \n, or empty. Lets --fix preserve original line endings
instead of normalizing CRLF to LF."""
m = _LINE_END_RE.search(raw)
return (raw[: m.start()], m.group()) if m else (raw, "")
def _split_keepends(text: str) -> list[str]:
r"""Split into lines on \r\n / \r / \n only -- NOT the extra Unicode
separators (U+2028/2029/000B/000C/0085) that str.splitlines() also breaks
on -- so a reported line number matches what an editor or grep counts. Each
piece keeps its trailing newline, like splitlines(keepends=True)."""
if not text:
return []
lines: list[str] = []
start = 0
for m in _LINE_SPLIT_RE.finditer(text):
lines.append(text[start : m.end()])
start = m.end()
if start < len(text):
lines.append(text[start:])
return lines
def fence_state(line: str, fence: tuple[str, int] | None) -> tuple[str, int] | None:
"""Track fenced-code state across lines, CommonMark-style. `fence` is None
outside a fence, or (char, length) inside one. An opener is indented <= 3
spaces and is >= 3 identical fence chars (``` or ~~~). A closer is the same
char, a run at least as long as the opener, and nothing else on the line.
Tracking char + length keeps mixed ``` / ~~~ blocks, and a ```` block that
contains a ``` line, from closing early; the indent rule stops a 4-space
indented ``` from being read as a fence and swallowing the text after it."""
indent = len(line) - len(line.lstrip(" "))
if indent > 3:
return fence
stripped = line[indent:]
if fence is None:
for ch in ("`", "~"):
if stripped.startswith(ch * 3):
run = len(stripped) - len(stripped.lstrip(ch))
return (ch, run)
return None
ch, length = fence
if stripped.startswith(ch * length):
run = len(stripped) - len(stripped.lstrip(ch))
if run >= length and not stripped[run:].strip():
return None
return fence
def detect_lang(text: str) -> str:
# Sample the language from real prose: fenced code, inline code, URLs, and
# link targets are stripped per line, so one kana inside a ```js block does
# not route an otherwise-Chinese document to ja. kana wins globally (any kana
# anywhere => ja, hence the early return); else Han => zh; Hangul is checked
# LAST so a mostly-Chinese text that merely quotes a Korean glyph stays zh.
saw_cjk = saw_hangul = False
fence: tuple[str, int] | None = None
for raw in _split_keepends(text):
body, _ = split_line_ending(raw)
was_in_fence = fence is not None
fence = fence_state(body, fence)
if fence is not None or was_in_fence:
continue
prose = "".join(c for c, m in zip(body, exempt_mask(body)) if not m)
if _KANA_RE.search(prose):
return "ja"
saw_cjk = saw_cjk or bool(_CJK_RE.search(prose))
saw_hangul = saw_hangul or bool(_HANGUL_RE.search(prose))
return "zh" if saw_cjk else "ko" if saw_hangul else "en"
class Finding:
def __init__(self, line: int, col: int, kind: str, snippet: str, suggestion: str):
self.line = line
self.col = col
self.kind = kind
self.snippet = snippet
self.suggestion = suggestion
def format(self, source: str) -> str:
return f"{source}:{self.line}:{self.col} [{self.kind}] {self.snippet!r} -> {self.suggestion}"
def check_dash(line: str, mask: list[bool], lineno: int, findings: list[Finding]) -> None:
"""Em/en dashes are banned in both zh and en output, so the rule is shared."""
for m in re.finditer("[\u2014\u2013]", line):
if overlaps_exempt(mask, m.start(), m.end()):
continue
findings.append(Finding(lineno, m.start() + 1, "dash", m.group(), "replace dash with comma / period"))
def check_latin_space(line: str, mask: list[bool], lineno: int, findings: list[Finding]) -> None:
"""A comma or semicolon glued to the next Latin letter with no space is a
spacing error in zh and en alike. Both sides are restricted to letters, so a
thousands separator (1,000) and a spaced enumeration (API, SDK) are spared."""
for m in re.finditer(r"[A-Za-z][,;][A-Za-z]", line):
if overlaps_exempt(mask, m.start(), m.end()):
continue
p = m.group()[1]
findings.append(Finding(lineno, m.start() + 2, "missing-space-after-punct", m.group(), f"add a space after '{p}'"))
def check_zh(line: str, mask: list[bool], lineno: int, findings: list[Finding]) -> None:
# A half-width mark next to a Han char -- directly, or across a markdown
# emphasis marker (** or _), so `**标签**:` is caught, not just `标签:`.
for m in re.finditer(f"[{CJK}][*_]*[{_HALFWIDTH}]", line):
if overlaps_exempt(mask, m.start(), m.end()):
continue
p = m.group()[-1]
findings.append(Finding(lineno, m.end(), "zh-halfwidth-punct", m.group(), f"{p} -> {FULLWIDTH[p]}")) # 1-based column of the offending punctuation
for m in re.finditer(f"[{_HALFWIDTH}][*_]*[{CJK}]", line):
if overlaps_exempt(mask, m.start(), m.end()):
continue
p = m.group()[0]
findings.append(Finding(lineno, m.start() + 1, "zh-halfwidth-punct", m.group(), f"{p} -> {FULLWIDTH[p]}")) # 1-based column of the offending punctuation
for m in re.finditer(f"[{CJK}][A-Za-z0-9]|[A-Za-z0-9][{CJK}]", line):
if overlaps_exempt(mask, m.start(), m.end()):
continue
findings.append(Finding(lineno, m.start() + 1, "zh-missing-space", m.group(), "add a space between CJK and Latin"))
check_dash(line, mask, lineno, findings)
for m in re.finditer(" ", line):
if overlaps_exempt(mask, m.start(), m.end()):
continue
findings.append(Finding(lineno, m.start() + 1, "zh-fullwidth-space", " ", "use a half-width space or remove it"))
# A half-width sentence-ender after Latin/digit/%/) that the Han-adjacent
# rules above miss, when the line is Chinese and the mark closes a clause
# (end-of-line, or a CJK char / CJK punctuation next). Narrow on purpose:
# only . ! ? -- never the comma -- so Latin lists like "API, SDK" are spared.
if _CJK_RE.search(line):
for m in re.finditer(rf"[A-Za-z0-9%)]([.!?])(?=$|[{CJK}{_CJK_PUNCT}])", line):
if overlaps_exempt(mask, m.start(), m.end()):
continue
p = m.group(1)
findings.append(Finding(lineno, m.start(1) + 1, "zh-halfwidth-punct", m.group(), f"{p} -> {FULLWIDTH[p]}"))
check_latin_space(line, mask, lineno, findings)
# A half-width paren hugging a Han char: zh prose wants full-width (). Kept
# conservative -- only when the paren directly touches a Han char -- so a paren
# around pure Latin or code, like foo(x), is left alone. Report only; --fix
# never rewrites parens, since whether (English) should be full-width is a
# house-style call left to the author.
for m in re.finditer(r"[()]", line):
if overlaps_exempt(mask, m.start(), m.end()):
continue
i = m.start()
left = line[i - 1] if i > 0 else ""
right = line[i + 1] if i + 1 < len(line) else ""
if _CJK_RE.match(left) or _CJK_RE.match(right):
paren = m.group()
findings.append(Finding(lineno, i + 1, "zh-halfwidth-paren", paren, f"{paren} -> {FULLPAREN[paren]}"))
def check_en(line: str, mask: list[bool], lineno: int, findings: list[Finding]) -> None:
check_dash(line, mask, lineno, findings)
check_latin_space(line, mask, lineno, findings)
for m in re.finditer(f"[{_CJK_PUNCT}]", line):
if overlaps_exempt(mask, m.start(), m.end()):
continue
findings.append(Finding(lineno, m.start() + 1, "en-fullwidth-punct", m.group(), "use ASCII punctuation"))
for m in re.finditer(r" +[,.;:!?]", line):
if overlaps_exempt(mask, m.start(), m.end()):
continue
findings.append(Finding(lineno, m.start() + 1, "en-space-before-punct", m.group(), "remove space before punctuation"))
def check_ja(line: str, mask: list[bool], lineno: int, findings: list[Finding]) -> None:
# Same emphasis-marker bridge as check_zh, so `**見出し**:` is caught.
for m in re.finditer(f"[{CJK}{KANA}][*_]*[{_HALFWIDTH}]|[{_HALFWIDTH}][*_]*[{CJK}{KANA}]", line):
if overlaps_exempt(mask, m.start(), m.end()):
continue
findings.append(Finding(lineno, m.start() + 1, "ja-ascii-punct", m.group(), "use full-width 、。!?:;"))
for m in re.finditer(f"[{CJK}{KANA}] +[A-Za-z]|[A-Za-z] +[{CJK}{KANA}]", line):
if overlaps_exempt(mask, m.start(), m.end()):
continue
findings.append(Finding(lineno, m.start() + 1, "ja-extra-space", m.group(), "remove space between Japanese and Latin"))
CHECKERS = {"zh": check_zh, "en": check_en, "ja": check_ja}
def fix_zh_line(line: str) -> str:
mask = exempt_mask(line)
chars = list(line)
for m in re.finditer(f"[{CJK}][*_]*[{_HALFWIDTH}]", line):
if not overlaps_exempt(mask, m.start(), m.end()):
chars[m.end() - 1] = FULLWIDTH[line[m.end() - 1]]
for m in re.finditer(f"[{_HALFWIDTH}][*_]*[{CJK}]", line):
if not overlaps_exempt(mask, m.start(), m.end()):
chars[m.start()] = FULLWIDTH[line[m.start()]]
# check_zh flags a half-width . ! ? that closes a Chinese clause after a
# Latin char / digit / % / ) ; replicate that detect-only rule here so --fix
# resolves it instead of reporting a finding it cannot fix. Same regex, so the
# fix range equals the detect range -- no new false positives.
if _CJK_RE.search(line):
for m in re.finditer(rf"[A-Za-z0-9%)]([.!?])(?=$|[{CJK}{_CJK_PUNCT}])", line):
if not overlaps_exempt(mask, m.start(), m.end()):
chars[m.start(1)] = FULLWIDTH[line[m.start(1)]]
# A full-width point mark carries its own spacing, so any half-width space
# hugging one (either side, however many) is wrong. Mark them, then compress
# chars and mask together so the CJK/Latin spacing pass keeps its alignment.
drop = [False] * len(chars)
for p, ch in enumerate(chars):
if ch in _CJK_PUNCT and not mask[p]:
for step in (1, -1):
q = p + step
while 0 <= q < len(chars) and chars[q] == " " and not mask[q]:
drop[q] = True
q += step
if any(drop):
chars = [c for i, c in enumerate(chars) if not drop[i]]
mask = [mk for i, mk in enumerate(mask) if not drop[i]]
result: list[str] = []
for i, ch in enumerate(chars):
if result and not (mask[i - 1] or mask[i]):
prev = result[-1]
if (_CJK_RE.match(prev) and _LATIN_RE.match(ch)) or (_LATIN_RE.match(prev) and _CJK_RE.match(ch)):
result.append(" ")
result.append(ch)
return "".join(result)
def fix_text(text: str, lang: str) -> str:
if lang != "zh":
return text
out: list[str] = []
fence: tuple[str, int] | None = None
for raw in _split_keepends(text):
body, ending = split_line_ending(raw)
was_in_fence = fence is not None
fence = fence_state(body, fence)
if fence is not None or was_in_fence:
out.append(raw)
continue
out.append(fix_zh_line(body) + ending)
return "".join(out)
def iter_findings(text: str, lang: str) -> list[Finding]:
findings: list[Finding] = []
checker = CHECKERS.get(lang)
if checker is None:
return findings
fence: tuple[str, int] | None = None
for lineno, raw in enumerate(_split_keepends(text), start=1):
line, _ = split_line_ending(raw)
was_in_fence = fence is not None
fence = fence_state(line, fence)
if fence is not None or was_in_fence:
continue
mask = exempt_mask(line)
checker(line, mask, lineno, findings)
# The same punctuation can match both the "Han before" and "Han after"
# rule; keep the first Finding per (line, col, kind).
deduped: dict[tuple[int, int, str], Finding] = {}
for f in findings:
deduped.setdefault((f.line, f.col, f.kind), f)
return list(deduped.values())
def main() -> int:
parser = argparse.ArgumentParser(description="Check punctuation / CJK mixing by locale.")
parser.add_argument("file", nargs="?", help="File to read (default: stdin)")
parser.add_argument("--lang", default="auto", choices=["zh", "en", "ja", "auto"])
parser.add_argument("--fix", action="store_true", help="Print corrected text to stdout")
args = parser.parse_args()
source = args.file or "-"
if args.file and args.file != "-":
# newline="" keeps original CRLF/CR so --fix does not normalize line endings.
try:
with open(args.file, encoding="utf-8", errors="replace", newline="") as f:
text = f.read()
except OSError as e:
# Exit 2 so an unreadable path is distinct from exit 1 (= findings to fix).
print(f"check_punctuation: cannot read {args.file}: {e.strerror or e}", file=sys.stderr)
return 2
else:
text = sys.stdin.buffer.read().decode("utf-8", "replace")
lang = detect_lang(text) if args.lang == "auto" else args.lang
if lang == "ko":
print("punctuation: ko locale is reserved (no rules yet); skipped", file=sys.stderr)
if args.fix:
sys.stdout.write(text)
return 0
if args.fix:
if lang != "zh":
print(f"punctuation: --fix has no rules for {lang}; text unchanged", file=sys.stderr)
sys.stdout.write(fix_text(text, lang))
return 0
findings = iter_findings(text, lang)
if findings:
for f in findings:
print(f.format(source))
return 1
print("punctuation: ok")
return 0
if __name__ == "__main__":
sys.exit(main())
scripts/check-punctuation.sh
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
exec python3 "$SCRIPT_DIR/check_punctuation.py" "$@"
SKILL.md
---
name: write
description: "Rewrites and polishes prose in Chinese or English, removes AI-like wording, and reviews product localization copy while preserving intent for drafts, docs, release notes, launch copy, and social posts. Use when users ask in any language to draft, rewrite, proofread, localize, polish release notes, remove AI-like wording, or prepare launch and social copy. Not for code comments, commit messages, or inline docs."
when_to_use: "帮我写, 改稿, 润色, 去AI味, 写一段, 审稿, 文档review, 本地化文案, 多语言文案, i18n copy, localization copy, check this document, 推特, twitter, X推文, tweet, social post, 连贯性, 段落连贯, draft, edit text, proofread, sound natural, polish, rewrite"
dispatch_intent: "Writing, editing prose, polish, release notes, launch/social copy, remove AI tone"
---
# Write: Cut the AI Taste
Prefix your first line with 🥷 inline, not as its own paragraph.
Strip AI patterns from prose and rewrite it to sound human. Do not improve vocabulary; remove the performance of improvement.
## Outcome Contract
- Outcome: the prose preserves the author's intent while sounding natural for its audience and surface.
- Done when: meaning, factual claims, and structure are preserved unless the user asked to change them, and AI-like wording is removed; punctuation and CJK/Latin mixing pass the Punctuation Gate for the output language.
- Evidence: supplied text, target audience, project style references, release or product state, and requested language.
- Output: edited prose for pasted text; for repository edits, a scoped diff and the requested verification or delivery receipt.
## Durable Context Preflight
See [references/durable-context.md](references/durable-context.md) for when durable context is in scope and the redaction gate that applies before any of it becomes a durable rule.
For `/write`: the supplied text and current release state override memory. Durable preferences can set brevity, tone, and social-post shape; they do not override the hard rule to edit in place, keep meaning intact, and avoid change lists unless the user explicitly asks.
## Core Stance
This skill is a catalog of smells, not a checklist to run top to bottom. Use it to recognize AI taste, then make judgment calls. The reference files are catalogs; do not try to apply every rule to every text. Applying more rules is not doing a better job.
- **Over-editing is failure, equal to under-editing.** If a sentence is already natural, clear, and stable, leave it. Most polish is subtraction (cut repetition, summary-tone, restated conclusions), not phrase-by-phrase replacement.
- **A piece has a speaker.** Smooth prose that could belong to anyone has lost something. Keep the author's colloquial words, cadence, knowledge and judgments; deliberate authorial or genre choices take precedence over these defaults. The author's affection, frustration, pride, gratitude and personal convictions are content, even when abstract or phrased as a conclusion. Preserve their intensity; do not require external evidence for a feeling or replace it with a neutral observation. Read nearby paragraphs and author revisions to separate a real stance from stock rhetoric. If that distinction is uncertain, keep the sentence. Do not invent emotion or turn "what I did" into "what you must do."
- **Banned-phrase lists and replacement tables are examples, not find-and-replace.** A flagged word that reads naturally in context stays. Match the smell, not the string. When source material exists, grep it before flagging a phrase: the author's own wording gets restored, not paraphrased.
- **Prefer fewer, stronger edits.** Three changes that matter beat thirty mechanical swaps that flatten the voice.
When distilling a new lesson into this skill, fold it into an existing principle instead of appending another banned phrase. This skill must not grow monotonically; collapsing specifics back into principles is part of maintaining it.
## Pre-flight
1. **Locate the text.** Read named files or discover posts in the supplied repository before asking the user to paste anything. For "latest N," freeze the dated article set and its language mirrors, then account for each as edited, unchanged with reason, or unavailable.
2. **Audience locked?** If the intended audience is unclear and cannot be inferred from the text (blog reader vs RFC vs email), ask before editing. Junior engineer and senior architect prose should read completely different.
3. **Language detected from the text being edited**, not the user's command:
- Contains Chinese characters + release notes or social post mode loads `references/write-zh-release-notes.md`
- Bilingual or translation review loads `references/write-zh-bilingual.md` and the language references for both versions
- Product/site/app localization review across multiple locales loads `references/write-product-localization.md`; also load `references/write-zh-bilingual.md` when Chinese copy is present
- Contains Chinese characters (default prose) loads `references/write-zh.md`
- Otherwise loads `references/write-en.md`
## Mode Picker
Default is a line-level rewrite of the supplied text. Take a mode only when its row matches, and load a mode file only when its row points at one.
| Ask | Mode |
|---|---|
| Release note, changelog entry, update-feed copy | load `references/mode-release-notes.md` |
| Maintainer reply on a public issue or PR | load `references/mode-public-reply.md` |
| Long draft with several sections, tables, or images that needs structural work | load `references/mode-long-form.md` |
| EN/CN pair to check for drift | [Bilingual Review](#bilingual-review-mode) |
| Product, site, or app copy across locales | [Product Localization Review](#product-localization-review-mode) |
| Document, PDF, or white paper to review | [Document Review](#document-review-mode) |
| Paragraphs that read disconnected | [Paragraph Coherence](#paragraph-coherence-mode) |
| Tweet, thread, or launch post | [Tweet / Social Post](#tweet--social-post-mode) |
## Bilingual Review Mode
Activate when: mixed Chinese/English, "Chinese copywriting", "bilingual consistency", "release notes"
Load `references/write-zh-bilingual.md`; it owns the judgment half that the Punctuation Gate script does not check.
## Product Localization Review Mode
Activate when: "本地化文案", "多语言文案", "localization copy", "i18n copy", product/site/app strings, release feed copy, runtime catalog, or a user asks whether localized copy feels native.
Load `references/write-product-localization.md` and follow its review procedure. If Chinese is one of the locales, also load `references/write-zh-bilingual.md`.
## Document Review Mode
Activate when: PDF, document, white paper, "review this document", "check this document", "审稿"
Review checklist:
- **Privacy scan**: Flag sensitive information whose disclosure is not authorized for the intended audience. Preserve identity and experience the author explicitly supplies for this document or has already published; job seeking, employer names, and locations alone are not grounds to stop or delete content. Resolve uncertain disclosure before exposing it.
- **Tone consistency**: Flag voice shifts, register mismatches, formulaic phrasing.
- **Bilingual validation**: For CN/EN pairs, confirm translation accuracy and terminology consistency. Apply Bilingual Review Mode rules.
- **Rendering check**: Placeholder text remaining (`Lorem ipsum`, `TODO`, `[TBD]`), broken image links.
- **Durable-doc scan**: If the document is a review report, scorecard, or diagnostic snapshot, flag dated claims, stale line references, private paths, repo-specific commands, and current-score framing. Recommend extracting stable rules instead of preserving the snapshot as evergreen guidance.
Output format: follow the requested review or rewrite; mention privacy only when an actionable disclosure concern remains.
## Paragraph Coherence Mode
Activate when: "连贯性", "段落连贯", "可读性", "coherence", "flow check", "段落顺不顺"
For review requests, report issues; for explicit rewrite or file-edit requests, apply the minimal authorized fixes. Check each paragraph for:
1. Flag transitions that abruptly shift topic without a signal.
2. Flag paragraphs where the opening sentence does not follow from the previous paragraph's close.
3. Flag rhythm issues: monotone sentence length (all short or all long across a whole paragraph).
4. Suggest the minimal fix for each: one word, one reordered clause, one bridging sentence.
Output: for review, a numbered list with paragraph locations and minimal fix suggestions; for rewriting, the revised text or scoped file changes. Do not ask again to apply already-authorized edits.
## Tweet / Social Post Mode
Activate when: "推特", "twitter", "X推文", "tweet", "social post", "折叠长度", "长文推特", "发文"
Load `references/write-zh-release-notes.md` for the five announcement rules (community lead, highlights over completeness, UX framing, one stance, native rhythm) and the casual invitation close. For English posts or projects without that community voice, keep the same structure in the project's own voice.
## Hard Rules
- **Meaning first, style second.** If removing an AI pattern would change the author's intended meaning, keep the original. Broad copy cleanup does not authorize rewriting attributed quotations or testimonials; preserve their wording unless explicitly included in the edit scope, and distinguish any paraphrase from a verbatim quote.
- **No silent restructuring.** Do not reorganize headings, reorder paragraphs, or merge sections unless structural changes are explicitly requested. Edit in place. Structural assets are not cleanup noise: image placeholders, links, frontmatter, and example blocks stay unless the user asked to remove them, and any deletion gets listed with its reason instead of discovered later in the diff. (Exception: `references/mode-long-form.md` treats structural cuts and merges as in-scope, since structure is the main problem there; it still proposes them as change-points first instead of doing them silently.)
- **No invented first-person experience.** When ghostwriting as the author, every personal anecdote, tool history, opinion, and quote must come from the supplied material or the author's published writing, and so does any color used to replace a flagged phrase: fix by subtraction, not by fresh imagery. The material lacking an example is a question to ask, not a gap to fill. Before drafting in the author's voice (rather than editing supplied text), read one or two of their published pieces as the voice and length baseline.
- **Material gate before drafting long-form.** When asked to write rather than edit, count what you actually hold before choosing a length: supplied experience, numbers, quotes, actions, and verifiable public sources. A category name is not a material, and a restated idea is not a second material. Reasoning connects material; it does not breed material. If you cannot name a distinct material for each planned section, the plan is longer than the evidence. Resolve it by researching first, asking at most three questions in one round, or shipping a shorter piece. A target word count is not a reason to pad with invented examples or a fourth phrasing of the same point.
- **Shorter than the first draft wants to be.** Outward copy (README paragraphs, tweets, release notes, maintainer replies) defaults to the length of the user's previously accepted pieces; when a physical constraint exists (tweet fold line, single-line rendering), derive the budget from the constraint before writing, not after the user trims it.
- **Artifact-grounded claims.** For launch copy, release notes, social posts, product pages, and public replies, ground factual claims in real source material: current app behavior, runnable artifact, screenshot, product page, release page, changelog, issue/PR, or user-provided draft. Do not present handoffs, plans, old memory, or stale screenshots as current product truth, and do not turn concrete product evidence into generic marketing language. Compare the draft against the shipping artifact and tighten until the two agree.
- **No em-dash.** Never produce em-dash (U+2014) or en-dash (U+2013) in Chinese or English output. Em-dash is the strongest AI-tone fingerprint in this style of writing. Use commas, periods, colons, or parentheses to break clauses. Hyphen-minus (`-`) inside compound words is allowed; replace it with a space or a period when possible. When editing a draft that contains em-dashes, replace every one before returning the text.
- **Match the requested handoff.** Pasted-text rewrites need no explanation. Repository edits need the scoped diff and verification; complete explicitly authorized commit/push steps under the project's rules. A prose-only output convention must not hide unfinished delivery.
## Punctuation Gate
Before returning any produced text (a rewrite, or generated release / reply / social copy), resolve the checker across install layouts and run it:
```bash
GATE=""
for candidate in \
"<skill-base-dir>/scripts/check-punctuation.sh" \
"<skill-base-dir>/skills/write/scripts/check-punctuation.sh"; do
[ -f "$candidate" ] && GATE="$candidate" && break
done
[ -f "${GATE:-}" ] || { echo "punctuation gate not found under the installed skill base; reinstall Waza" >&2; exit 1; }
bash "$GATE" --lang <zh|en|ja|auto> <file> # or pipe text via stdin
```
Replace `<skill-base-dir>` with the installed Write skill or Waza dispatcher directory. The first path covers direct/plugin installs; the second covers the inlined-root release ZIP.
It enforces character-level punctuation by locale (half/full-width marks, CJK/Latin spacing, em/en dashes) and skips code, inline code, URLs, and markdown link targets, so it never fires on code; the script header documents the exact rule set. Fix every finding while preserving meaning; `--fix` rewrites only the zero-ambiguity zh cases to stdout. `--lang auto` classifies the whole input by fixed priority: any kana routes to ja, else any CJK to zh, else any Hangul to ko (reserved, skipped), else en, so a mostly-Chinese text that merely quotes a Korean glyph still routes to zh; pass an explicit `--lang` for mixed-locale or predominantly-English text. The checker owns character-level punctuation only; quote direction and other judgment calls stay with you and the reference files.
## Gotchas
| What happened | Rule |
|---|---|
| Used formal register for a blog draft | Match the target audience's register. Blog is conversational, not academic. |
| Applied Chinese/English spacing rules to a pure-English text | Bilingual spacing rules (半角/全角) only apply when the text mixes Chinese and English |
| User flagged one word as "not my voice"; only that instance was fixed | A flagged word marks a smell class, not a typo. Sweep the whole text for the same class (same register, same template shape) before returning |
## Output
Follow the Outcome Contract. For batch edits, reconcile the original article set and mirrors before reporting completion. State missing source or verification without treating it as a pass.