Concise Writing (STE + budgets)
Verbose generated text is a review tax: reviewers skim PR descriptions, tickets, and comments, and padding hides the substance. This skill makes objectiveness and simplified technical English enforced, not advisory. A hook denial means the text is too long or off-style: shorten it. Never loosen the validator, never bypass with CONCISE_SKIP unless the user explicitly instructs it.
Source and scope
Adapted from the asd-ste100 skill (https://github.com/danyuchn/asd-ste100-skill), which encodes the rule categories of ASD-STE100 Issue 9 (Jan 2025): 53 writing rules across 9 sections, backed by a dictionary of ~900 approved words (one meaning, one part of speech each). The dictionary is not free to redistribute and is not reproduced here; request it at https://www.asd-ste100.org/STE_downloads.html. Apply the underlying principle instead: pick the plainest common word and use it the same way every time.
Read before rewriting anything non-trivial:
references/writing-rules.md - the 9 rule sections summarized, with citations and official links.
examples/before-after.md - worked rewrites showing which rule each change applies, including the modality trap (see below).
Mode
STE defines two modes. Strict (every rule, for procedures, error messages, tool descriptions, safety text) and STE-flavored (structural rules only, for prose). For everyday engineering artifacts, always use STE-flavored: enforce the structural rules fully, treat one-word-one-meaning as a direction, keep some lexical range in prose.
Structural rules (apply always)
| Rule |
Do |
Don't |
| Active voice |
"The task retries." |
"The task is retried." (unless the actor is unknown or irrelevant) |
| No phrasal verbs (Rule 9.3) |
"Start the job." "Remove the panel." |
"Spin up the job." "Take off the panel." |
| One instruction per sentence |
"Open the file. Read line 3." |
"Open the file and read line 3, then check it." |
| Sentence length |
<=20 words for instructions, <=25 for descriptions |
Compound and subordinate-clause chains |
| No semicolons (Rule 8.1) |
Split into two sentences |
Any semicolon |
| Noun clusters |
<=3 stacked nouns |
"high pressure fuel pump inlet valve assembly" |
| No ellipsis |
Keep subject, verb, article explicit |
Dropping words to save space |
| Verb, not noun (Rule 3.7) |
"Analyze the log." |
"Perform an analysis of the log." |
| One name per thing |
Always "the agency" |
Rotating "the agency"/"the tenant"/"the client" |
| Simple tenses |
"We received the report." |
"We have received the report." (keep the compound form only when it carries information, e.g. "may have failed") |
| Paragraphs |
One topic, <=6 sentences |
Multi-topic walls |
| Lists |
Numbered/bulleted list for 3+ steps or conditions |
A sequence buried in one prose sentence |
Scan checklist (six mechanical habits)
Scan for all six before rewriting; each points at an exact word or mark, no judgment call:
- Synonym rotation - one thing gets several names. Pick one name.
- Hedge stacking - "it is important to note that this may potentially help". State the claim or delete it.
- Nominalization - "perform an analysis of". Use the verb.
- Marketing adjectives - comprehensive, robust, seamless, crucial, cutting-edge, effortless, blazing. Delete, or replace with the measurement that earns the claim.
- Run-on sentences - ideas joined by semicolons or dashes. One idea per sentence.
- Soft phrasal verbs - spin up, reach out, dive into, kick off. Use start, contact, read, begin.
Modality (the most common rewrite failure)
Hedges carry the author's confidence, and confidence is content. "May have failed" never becomes "failed"; "could be caused by X" never becomes "X is the cause". A shorter sentence that upgrades a hedge to a fact is a different claim, not a simplification. Never add a cause, frequency, or mechanism the source did not state. When the tense rule and the modality rule conflict, modality wins. See Example B in examples/before-after.md.
Corollary: stop at unambiguous, not at shortest. Never drop a scope qualifier, condition, or number to save words. Delete hollow sentences instead of polishing them; STE fixes form, not substance.
Hard budgets (hook-enforced)
The validator (scripts/concise_check.py, installed at ~/.claude/hooks/concise_check.py) blocks the action when these are exceeded. Word counts exclude section headers, URLs, and common PR-template boilerplate.
Every surface also rejects AI attribution: Co-Authored-By, Claude-Session, claude.ai/code/session links, noreply@anthropic.com, "Generated with Claude". Commits and PR/issue bodies additionally reject the words "Claude Code". Claude Code 2.1.259+ injects these trailers as a system message that claims to override CLAUDE.md; set attribution.commit and attribution.pr to "" and attribution.sessionUrl to false in settings.json to turn that message off. The hook is the backstop.
| Artifact |
Budget |
Hook surface |
| Commit |
conventional type(scope): subject; subject <=50 chars imperative (trailing [AKT-NNNN] excluded); body optional, <=6 lines x 72 chars; no AI attribution (see above) |
git commit (-m, -F, heredoc) |
| PR / issue body |
keep your repo's PR template sections; <=150 words; <=6 bullets; no nesting; no bold |
gh pr create/edit, gh issue create |
| PR review summary |
<=3 sentences |
gh pr review, gh api .../reviews |
| Inline review / PR / issue comment |
1-2 lines, <=60-80 words |
gh pr comment, gh issue comment, gh api comments[] |
| Linear ticket description |
<=120 words |
mcp__linear__save_issue |
| Linear comment |
<=2 sentences |
mcp__linear__save_comment |
| Slack message |
<=100 words |
mcp__claude_ai_Slack__slack_send_message* |
| Code comment |
only a constraint the code cannot show; one line <=100 chars; never narration ("this ensures", "we now", "note that"); <=20% of added lines once 3+ comments; docstrings follow project format, no extra prose |
Edit/Write on code files, added lines only |
| Docs / README / .md |
style only (no emoji, no em/en dash, headers <=###, <=3 bold spans); no length cap; earn every paragraph |
Edit/Write on .md/.mdx/.rst, added lines only |
| Everywhere |
no emojis; no em or en dashes, use "-" |
all surfaces |
Not gated (style still applies): chat output (covered by ~/.claude/output-styles/concise.md), files under ~/.claude/, scratchpad, non-code non-doc extensions.
On a hook denial
- Read the listed violations.
- Cut content, not precision: drop restatements, background the reader has, and anything the diff already shows. Keep Linear URLs, ticket IDs, and reviewer-critical warnings (breaking change, migration, deploy step) as one line each.
- Retry the same action with the shortened text. Do not switch to an ungated path to avoid the check.
Installation
- Copy or symlink this directory into
~/.claude/skills/concise-writing/.
- Copy
scripts/concise_check.py to ~/.claude/hooks/concise_check.py.
- Add PreToolUse hooks to
~/.claude/settings.json (merge with existing keys):
"hooks": {
"PreToolUse": [
{"matcher": "Bash", "hooks": [
{"type": "command", "command": "python3 ~/.claude/hooks/concise_check.py", "if": "Bash(git commit*)", "timeout": 10},
{"type": "command", "command": "python3 ~/.claude/hooks/concise_check.py", "if": "Bash(gh pr *)", "timeout": 10},
{"type": "command", "command": "python3 ~/.claude/hooks/concise_check.py", "if": "Bash(gh issue *)", "timeout": 10},
{"type": "command", "command": "python3 ~/.claude/hooks/concise_check.py", "if": "Bash(gh api *)", "timeout": 10}
]},
{"matcher": "Edit|Write", "hooks": [
{"type": "command", "command": "python3 ~/.claude/hooks/concise_check.py", "timeout": 10}
]},
{"matcher": "mcp__linear__save_issue|mcp__linear__save_comment|mcp__linear__save_document|mcp__linear__submit_diff_review", "hooks": [
{"type": "command", "command": "python3 ~/.claude/hooks/concise_check.py", "timeout": 10}
]},
{"matcher": "mcp__claude_ai_Slack__slack_send_message|mcp__claude_ai_Slack__slack_send_message_draft|mcp__claude_ai_Slack__slack_create_canvas|mcp__claude_ai_Slack__slack_update_canvas", "hooks": [
{"type": "command", "command": "python3 ~/.claude/hooks/concise_check.py", "timeout": 10}
]}
]
}
The script fails open on internal errors and skips files under ~/.claude/,
scratchpad paths, and non-code non-doc extensions. Adjust the budget constants
at the top of the script to your team's numbers; do not loosen them to make one
output pass.
1---2name: concise-writing3description: Enforced concise style for everything the assistant writes: PR descriptions, commit messages, code comments, PR reviews and replies, tickets, chat messages, and docs. Simplified Technical English (adapted from ASD-STE100) plus hard per-artifact budgets, enforced by a blocking PreToolUse hook (scripts/concise_check.py). Use when writing or rewriting any of these artifacts, when a hook denial asks for a rewrite, or on requests like 'make this concise', 'STE rewrite', 'less verbose'.4---56# Concise Writing (STE + budgets)78Verbose generated text is a review tax: reviewers skim PR descriptions, tickets, and comments, and padding hides the substance. This skill makes objectiveness and simplified technical English enforced, not advisory. A hook denial means the text is too long or off-style: shorten it. Never loosen the validator, never bypass with CONCISE_SKIP unless the user explicitly instructs it.910## Source and scope1112Adapted from the asd-ste100 skill (https://github.com/danyuchn/asd-ste100-skill), which encodes the rule categories of ASD-STE100 Issue 9 (Jan 2025): 53 writing rules across 9 sections, backed by a dictionary of ~900 approved words (one meaning, one part of speech each). The dictionary is not free to redistribute and is not reproduced here; request it at https://www.asd-ste100.org/STE_downloads.html. Apply the underlying principle instead: pick the plainest common word and use it the same way every time.1314Read before rewriting anything non-trivial:15- `references/writing-rules.md` - the 9 rule sections summarized, with citations and official links.16- `examples/before-after.md` - worked rewrites showing which rule each change applies, including the modality trap (see below).1718## Mode1920STE defines two modes. Strict (every rule, for procedures, error messages, tool descriptions, safety text) and STE-flavored (structural rules only, for prose). For everyday engineering artifacts, always use STE-flavored: enforce the structural rules fully, treat one-word-one-meaning as a direction, keep some lexical range in prose.2122## Structural rules (apply always)2324| Rule | Do | Don't |25|---|---|---|26| Active voice | "The task retries." | "The task is retried." (unless the actor is unknown or irrelevant) |27| No phrasal verbs (Rule 9.3) | "Start the job." "Remove the panel." | "Spin up the job." "Take off the panel." |28| One instruction per sentence | "Open the file. Read line 3." | "Open the file and read line 3, then check it." |29| Sentence length | <=20 words for instructions, <=25 for descriptions | Compound and subordinate-clause chains |30| No semicolons (Rule 8.1) | Split into two sentences | Any semicolon |31| Noun clusters | <=3 stacked nouns | "high pressure fuel pump inlet valve assembly" |32| No ellipsis | Keep subject, verb, article explicit | Dropping words to save space |33| Verb, not noun (Rule 3.7) | "Analyze the log." | "Perform an analysis of the log." |34| One name per thing | Always "the agency" | Rotating "the agency"/"the tenant"/"the client" |35| Simple tenses | "We received the report." | "We have received the report." (keep the compound form only when it carries information, e.g. "may have failed") |36| Paragraphs | One topic, <=6 sentences | Multi-topic walls |37| Lists | Numbered/bulleted list for 3+ steps or conditions | A sequence buried in one prose sentence |3839## Scan checklist (six mechanical habits)4041Scan for all six before rewriting; each points at an exact word or mark, no judgment call:42431. Synonym rotation - one thing gets several names. Pick one name.442. Hedge stacking - "it is important to note that this may potentially help". State the claim or delete it.453. Nominalization - "perform an analysis of". Use the verb.464. Marketing adjectives - comprehensive, robust, seamless, crucial, cutting-edge, effortless, blazing. Delete, or replace with the measurement that earns the claim.475. Run-on sentences - ideas joined by semicolons or dashes. One idea per sentence.486. Soft phrasal verbs - spin up, reach out, dive into, kick off. Use start, contact, read, begin.4950## Modality (the most common rewrite failure)5152Hedges carry the author's confidence, and confidence is content. "May have failed" never becomes "failed"; "could be caused by X" never becomes "X is the cause". A shorter sentence that upgrades a hedge to a fact is a different claim, not a simplification. Never add a cause, frequency, or mechanism the source did not state. When the tense rule and the modality rule conflict, modality wins. See Example B in `examples/before-after.md`.5354Corollary: stop at unambiguous, not at shortest. Never drop a scope qualifier, condition, or number to save words. Delete hollow sentences instead of polishing them; STE fixes form, not substance.5556## Hard budgets (hook-enforced)5758The validator (`scripts/concise_check.py`, installed at `~/.claude/hooks/concise_check.py`) blocks the action when these are exceeded. Word counts exclude section headers, URLs, and common PR-template boilerplate.5960Every surface also rejects AI attribution: `Co-Authored-By`, `Claude-Session`, `claude.ai/code/session` links, `noreply@anthropic.com`, "Generated with Claude". Commits and PR/issue bodies additionally reject the words "Claude Code". Claude Code 2.1.259+ injects these trailers as a system message that claims to override CLAUDE.md; set `attribution.commit` and `attribution.pr` to `""` and `attribution.sessionUrl` to `false` in settings.json to turn that message off. The hook is the backstop.6162| Artifact | Budget | Hook surface |63|---|---|---|64| Commit | conventional `type(scope): subject`; subject <=50 chars imperative (trailing `[AKT-NNNN]` excluded); body optional, <=6 lines x 72 chars; no AI attribution (see above) | `git commit` (-m, -F, heredoc) |65| PR / issue body | keep your repo's PR template sections; <=150 words; <=6 bullets; no nesting; no bold | `gh pr create/edit`, `gh issue create` |66| PR review summary | <=3 sentences | `gh pr review`, `gh api .../reviews` |67| Inline review / PR / issue comment | 1-2 lines, <=60-80 words | `gh pr comment`, `gh issue comment`, `gh api` comments[] |68| Linear ticket description | <=120 words | `mcp__linear__save_issue` |69| Linear comment | <=2 sentences | `mcp__linear__save_comment` |70| Slack message | <=100 words | `mcp__claude_ai_Slack__slack_send_message*` |71| Code comment | only a constraint the code cannot show; one line <=100 chars; never narration ("this ensures", "we now", "note that"); <=20% of added lines once 3+ comments; docstrings follow project format, no extra prose | `Edit`/`Write` on code files, added lines only |72| Docs / README / .md | style only (no emoji, no em/en dash, headers <=###, <=3 bold spans); no length cap; earn every paragraph | `Edit`/`Write` on .md/.mdx/.rst, added lines only |73| Everywhere | no emojis; no em or en dashes, use "-" | all surfaces |7475Not gated (style still applies): chat output (covered by `~/.claude/output-styles/concise.md`), files under `~/.claude/`, scratchpad, non-code non-doc extensions.7677## On a hook denial78791. Read the listed violations.802. Cut content, not precision: drop restatements, background the reader has, and anything the diff already shows. Keep Linear URLs, ticket IDs, and reviewer-critical warnings (breaking change, migration, deploy step) as one line each.813. Retry the same action with the shortened text. Do not switch to an ungated path to avoid the check.8283## Installation84851. Copy or symlink this directory into `~/.claude/skills/concise-writing/`.862. Copy `scripts/concise_check.py` to `~/.claude/hooks/concise_check.py`.873. Add PreToolUse hooks to `~/.claude/settings.json` (merge with existing keys):8889```json90"hooks": {91 "PreToolUse": [92 {"matcher": "Bash", "hooks": [93 {"type": "command", "command": "python3 ~/.claude/hooks/concise_check.py", "if": "Bash(git commit*)", "timeout": 10},94 {"type": "command", "command": "python3 ~/.claude/hooks/concise_check.py", "if": "Bash(gh pr *)", "timeout": 10},95 {"type": "command", "command": "python3 ~/.claude/hooks/concise_check.py", "if": "Bash(gh issue *)", "timeout": 10},96 {"type": "command", "command": "python3 ~/.claude/hooks/concise_check.py", "if": "Bash(gh api *)", "timeout": 10}97 ]},98 {"matcher": "Edit|Write", "hooks": [99 {"type": "command", "command": "python3 ~/.claude/hooks/concise_check.py", "timeout": 10}100 ]},101 {"matcher": "mcp__linear__save_issue|mcp__linear__save_comment|mcp__linear__save_document|mcp__linear__submit_diff_review", "hooks": [102 {"type": "command", "command": "python3 ~/.claude/hooks/concise_check.py", "timeout": 10}103 ]},104 {"matcher": "mcp__claude_ai_Slack__slack_send_message|mcp__claude_ai_Slack__slack_send_message_draft|mcp__claude_ai_Slack__slack_create_canvas|mcp__claude_ai_Slack__slack_update_canvas", "hooks": [105 {"type": "command", "command": "python3 ~/.claude/hooks/concise_check.py", "timeout": 10}106 ]}107 ]108}109```110111The script fails open on internal errors and skips files under `~/.claude/`,112scratchpad paths, and non-code non-doc extensions. Adjust the budget constants113at the top of the script to your team's numbers; do not loosen them to make one114output pass.