-
Notifications
You must be signed in to change notification settings - Fork 21
Overhaul docs tone guidance and rewrite platform overview as example #606
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
131ebae
ead37b6
1625d09
7c9d2cb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -27,7 +27,8 @@ Focus on: | |
| 4. **Code snippets**: Verify that any code examples, commands, or configuration snippets are correct and will work as documented. If you're unsure about technical details, use the `answer_question` skill to verify against the docs or search the source code. | ||
| 5. **Astro Starlight structure**: Verify `src/sidebar.ts` updates if files were added, moved, or renamed, and that redirects are added to the `redirects` array in `vercel.json` when needed. | ||
| 6. **Product name variables**: Check whether any product names with a corresponding entry in `src/data/vars.ts` are hardcoded as literal strings instead of using `{VARS.KEY}` (prose) or `{{TOKEN}}` (frontmatter). Key strings to watch for: "Oz CLI", "Oz web app", "oz.warp.dev", "Oz dashboard", "Oz run". Flag as `⚠️ [IMPORTANT]` if a new file adds these without using the variable system. For existing files, flag as `💡 [SUGGESTION]`. | ||
| 7. **AEO/source-data fit**: | ||
| 7. **Tone and AI-isms**: Flag marketing buzzwords (seamless, powerful, robust, comprehensive, leverage, streamline), meta-openers ("This page covers/explains/walks through..."), restated cause-and-effect ("This process ensures..."), recap lines, consecutive callouts or more than one callout per section, and internal-architecture detail the reader can't act on (orchestrators, control planes, lifecycle states). Reference the "Voice & tone" section of `AGENTS.md`. Use `💡 [SUGGESTION]` for isolated instances; use `⚠️ [IMPORTANT]` when the pattern is pervasive in new content. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This covers tone thoroughly but never mentions length or the "Cut again" deletion pass. The only adjacent word in the whole skill is "Scannability." So a reviewing agent flags Suggest adding length as its own item: flag pages that could be materially shorter, and check that a deletion pass happened. |
||
| 8. **AEO/source-data fit**: | ||
|
|
||
| Provide actionable, constructive feedback. Focus on documentation quality issues, not code bugs. | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -195,6 +195,46 @@ | |
| (r"\b[Aa]mbient [Aa]gents?\b", "Use 'cloud agent(s)' — 'ambient' is no longer a product term"), | ||
| ] | ||
|
|
||
| # Tone: AI-generated-sounding words from AGENTS.md → Voice & tone → "Words to | ||
| # avoid". Report-only (never auto-fixed): every hit needs a human rewrite that | ||
| # names the specific capability, not a mechanical substitution. | ||
| # | ||
| # Deliberately narrower than the prose guidance. Words with legitimate | ||
| # technical uses in these docs are excluded so warnings stay trustworthy: | ||
| # "harness" (agent harness), "unlock" (login/keychain unlock), "elevate(d)" | ||
| # (elevated permissions), and "journey" stay out of the lint and are covered | ||
| # by AGENTS.md only. | ||
| TONE_BUZZWORDS: List[Tuple[str, str]] = [ | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The lint covers 2 of the 6 filler frames in
The four intentional exclusions are documented in the comment above; these six just aren't here, which reads as oversight rather than judgment. Either add them or note why not. |
||
| (r"\bseamless(?:ly)?\b", "Marketing adjective; describe the specific behavior instead"), | ||
| (r"\beffortless(?:ly)?\b", "Marketing adjective; describe the specific behavior instead"), | ||
| (r"\bpowerful\b", "Marketing adjective; name what the feature does instead"), | ||
| (r"\brobust\b", "Marketing adjective; name what the feature does instead"), | ||
| (r"\bcomprehensive(?:ly)?\b", "Marketing adjective; say what is included instead"), | ||
| (r"\bcutting-edge\b", "Marketing adjective; delete it or name the capability"), | ||
| (r"\bgame-chang\w+\b", "Marketing adjective; delete it or name the capability"), | ||
| (r"\bsupercharg\w+\b", "Marketing verb; name the specific improvement instead"), | ||
| (r"\bleverag(?:e|es|ed|ing)\b", "Use 'use'"), | ||
| (r"\bstreamlin(?:e|es|ed|ing)\b", "Say what gets shorter or removed instead"), | ||
| (r"\bempower(?:s|ed|ing)?\b", "Use 'let' or name the capability"), | ||
| (r"\bdelv(?:e|es|ed|ing)\b", "Use 'cover' or name the topic directly"), | ||
| (r"\btapestry\b", "Abstract metaphor; name the concrete thing"), | ||
| (r"\btestament to\b", "Filler phrase; state the fact directly"), | ||
| (r"\b(?:it'?s|it is) (?:important to note|worth noting)\b", "Filler frame; cut it and state the fact directly"), | ||
| ] | ||
|
|
||
| # Tone: meta-text that narrates the page instead of stating the thing itself. | ||
| # AGENTS.md → Voice & tone → "Every sentence earns its place". | ||
| META_OPENER = re.compile( | ||
| r"\bThis (?:page|guide|section|article|document) (?:covers|explains|describes|walks(?: you)? through)\b" | ||
| ) | ||
|
|
||
| # Starlight aside fences, for the callout-budget checks. | ||
| CALLOUT_OPEN = re.compile(r"^\s*:::(note|tip|caution|danger)\b") | ||
| CALLOUT_CLOSE = re.compile(r"^\s*:::\s*$") | ||
| # More callouts than this on one page almost always means caveats that belong | ||
| # in body prose. AGENTS.md allows at most one callout per section. | ||
| CALLOUT_PAGE_BUDGET = 4 | ||
|
|
||
| # Action verbs that precede UI elements (should be bold, not backtick) | ||
| UI_ACTION_VERBS = r"(?:click|select|toggle|enable|disable|choose|check|uncheck|expand|collapse|open|close|tap)" | ||
|
|
||
|
|
@@ -1274,6 +1314,111 @@ def check_factory_proper_noun(lines: List[str], filepath: str) -> List[Issue]: | |
| return issues | ||
|
|
||
|
|
||
| # --------------------------------------------------------------------------- | ||
| # Tone checks (report-only, never auto-fixed) | ||
| # --------------------------------------------------------------------------- | ||
|
|
||
| def _strip_inline_code(line: str) -> str: | ||
| """Remove inline code spans so CLI flags and API fields never trip tone checks.""" | ||
| return re.sub(r"`[^`]*`", "", line) | ||
|
|
||
|
|
||
| def check_tone_buzzwords(lines: List[str], filepath: str) -> List[Issue]: | ||
| """Flag AI-ism buzzwords (AGENTS.md → Voice & tone → Words to avoid). | ||
|
|
||
| Report-only: the fix is a rewrite that names the specific capability, | ||
| which cannot be automated. Frontmatter is scanned too — a buzzword in a | ||
| description is still a buzzword in search results. | ||
| """ | ||
| issues = [] | ||
| in_code_block = False | ||
| for i, line in enumerate(lines, 1): | ||
| stripped = line.strip() | ||
| if stripped.startswith("```"): | ||
| in_code_block = not in_code_block | ||
| continue | ||
| if in_code_block: | ||
| continue | ||
| prose = _strip_inline_code(line) | ||
| for pattern, suggestion in TONE_BUZZWORDS: | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The three new checks ship without tests. Regexes over prose are exactly where false positives creep in. A |
||
| for m in re.finditer(pattern, prose, re.IGNORECASE): | ||
| issues.append(Issue( | ||
| filepath, i, "tone-buzzword", | ||
| f'"{m.group(0)}": {suggestion}', | ||
| "warning", | ||
| )) | ||
| return issues | ||
|
|
||
|
|
||
| def check_meta_openers(lines: List[str], filepath: str) -> List[Issue]: | ||
| """Flag meta-text that narrates the page ("This page covers..."). | ||
|
|
||
| The title and description already frame the page; body prose should state | ||
| the thing itself. AGENTS.md → Voice & tone → Every sentence earns its place. | ||
| """ | ||
| issues = [] | ||
| in_code_block = False | ||
| for i, line in enumerate(lines, 1): | ||
| stripped = line.strip() | ||
| if stripped.startswith("```"): | ||
| in_code_block = not in_code_block | ||
| continue | ||
| if in_code_block: | ||
| continue | ||
| if META_OPENER.search(_strip_inline_code(line)): | ||
| issues.append(Issue( | ||
| filepath, i, "tone-meta-opener", | ||
| "Meta-text that narrates the page ('This page covers...'). " | ||
| "Cut it and state the thing itself", | ||
| "warning", | ||
| )) | ||
| return issues | ||
|
|
||
|
|
||
| def check_callout_density(lines: List[str], filepath: str) -> List[Issue]: | ||
| """Flag back-to-back callouts and pages over the callout budget. | ||
|
|
||
| AGENTS.md → Callouts and hints: never consecutive, at most one per | ||
| section. Per-page count is the lintable proxy for the per-section rule. | ||
| """ | ||
| issues = [] | ||
| in_code_block = False | ||
| in_callout = False | ||
| open_lines: List[int] = [] | ||
| last_close_line: Optional[int] = None | ||
| for i, line in enumerate(lines, 1): | ||
| stripped = line.strip() | ||
| if stripped.startswith("```"): | ||
| in_code_block = not in_code_block | ||
| continue | ||
| if in_code_block: | ||
| continue | ||
| if not in_callout and CALLOUT_OPEN.match(line): | ||
| open_lines.append(i) | ||
| if last_close_line is not None and all( | ||
| not lines[j].strip() for j in range(last_close_line, i - 1) | ||
| ): | ||
| issues.append(Issue( | ||
| filepath, i, "callout-consecutive", | ||
| "Two callouts back to back; merge them or move one into " | ||
| "body prose (AGENTS.md → Callouts and hints)", | ||
| "warning", | ||
| )) | ||
| in_callout = True | ||
| continue | ||
| if in_callout and CALLOUT_CLOSE.match(line): | ||
| in_callout = False | ||
| last_close_line = i | ||
| if len(open_lines) > CALLOUT_PAGE_BUDGET: | ||
| issues.append(Issue( | ||
| filepath, open_lines[CALLOUT_PAGE_BUDGET], "callout-density", | ||
| f"{len(open_lines)} callouts on one page; keep to at most one per " | ||
| "section and move the rest into body prose", | ||
| "warning", | ||
| )) | ||
| return issues | ||
|
|
||
|
|
||
| # Cache glossary terms once at module level | ||
| _glossary_cache: Optional[set] = None | ||
|
|
||
|
|
@@ -1299,6 +1444,9 @@ def run_all_checks(filepath: Path) -> List[Issue]: | |
| issues.extend(check_screenshot_widths(lines, str(filepath))) | ||
| issues.extend(check_video_embed_titles(lines, str(filepath))) | ||
| issues.extend(check_callout_syntax(lines, str(filepath))) | ||
| issues.extend(check_tone_buzzwords(lines, str(filepath))) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| issues.extend(check_meta_openers(lines, str(filepath))) | ||
| issues.extend(check_callout_density(lines, str(filepath))) | ||
| issues.extend(check_product_casing(lines, str(filepath))) | ||
| issues.extend(check_oz_terms(lines, str(filepath))) | ||
| issues.extend(check_deprecated_terms(lines, str(filepath))) | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've been seeing a lot of compound sentences with both em-dashes and colons. These stylistic choices are okay but sparingly, and I'm seeing them in more than 50% of paragraphs on new docs. Maybe we could add a "don't" about overuse of colons/em-dashes, and preferring concise, direct sentences.