Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 10 additions & 3 deletions .agents/rules/oz-style-guidelines.md
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,8 @@ Avoid framing Warp as:

## Voice and Style

The authoritative tone rules live in the "Voice & tone" section of the docs style guide (`AGENTS.md`), including the lists of AI-generated-sounding words and structural patterns to avoid. The points below cover positioning-specific guidance.

### Do's:
- **Talk about outcomes, not hype** - Show what changes when agents are reliable and orchestrated
- **Emphasize control and trust** - Warp augments developers, doesn't replace them
Expand All @@ -159,6 +161,9 @@ Avoid framing Warp as:
- Don't use corporate or disconnected language
- Skip theoretical posts without practical value
- Don't list features without context or problem-solving
- Don't use AI-slop buzzwords ("seamless," "powerful," "robust," "leverage," "streamline"): name the specific capability instead

Copy link
Copy Markdown
Contributor

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.

- Don't open pages with meta-text ("This page covers...") or narrate the page's structure
- Don't document internal architecture (orchestrators, control planes, lifecycle states): describe the user-visible model the reader acts on

## Writing Guidelines

Expand All @@ -172,9 +177,11 @@ Avoid framing Warp as:
- Descriptive text and captions end in periods

### Preferred Phrases
- ✅ "Ask Oz to..."
- ✅ "Oz can help you..."
- ✅ "What would you like Oz to do?"
- ✅ "Ask the agent to..."
- ✅ "Run an agent on the Automation Platform"
- ✅ "The Automation Platform can run this on a schedule"
- ❌ "Ask Oz to..." (stale: Oz was renamed to the Automation Platform on 2026-08-18)
- ❌ "Ask the Automation Platform to..." (you ask an agent, not a platform)

## Problem Framing

Expand Down
9 changes: 7 additions & 2 deletions .agents/skills/draft_docs/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,9 @@ These rules are frequently violated by agents. Apply them carefully during draft
- **Cover team-wide and admin effects** — For integrations and team features, state who can install, whether every teammate gets access immediately, and any per-user auth or admin steps on the external system. ✅ "A Jira admin must install the app; each teammate links their own account for run attribution." ❌ setup steps that only describe the installer's happy path
- **Section order follows reader chronology** — Prerequisites and requirements before setup, setup before usage, usage before advanced options. ✅ `## Prerequisites` → `## Set up the integration` → `## Start a run` ❌ setup steps before the reader knows what they need
- **Keep error messages out of the main flow** — Do not weave full error strings through conceptual or procedural sections. Put them in a dedicated `## Troubleshooting` section near the end, formatted symptom → cause → fix. ✅ one Troubleshooting section with the exact error as a bold lead-in ❌ repeating the same error callout after every step
- **Use callouts sparingly** — Prefer body prose. At most one or two callouts per page unless the content type template requires more. ✅ a single `:::note` for a non-obvious prerequisite ❌ a `:::note` / `:::tip` after every subsection
- **Use callouts sparingly** — Prefer body prose. At most one or two callouts per page unless the content type template requires more, never two callouts back to back, and at most one per section. ✅ a single `:::note` for a non-obvious prerequisite ❌ a `:::note` / `:::tip` after every subsection
- **No AI-ism buzzwords or meta-openers** — Never open with "This page covers/explains/walks through..."; state the thing itself. Avoid marketing adjectives (seamless, powerful, robust, comprehensive), inflated verbs (leverage, streamline, empower, unlock), restated cause-and-effect ("This process ensures..."), and recap lines. See AGENTS.md → Voice & tone for the full lists. ✅ "Run agents directly in your GitHub Actions workflows using `oz-agent-action`." ❌ "This page covers how the integration works, how to set it up, and common automation patterns."
- **Document the user-visible model, not internal architecture** — Internal components (orchestrators, control planes, lifecycle state machines) get at most one sentence, and only when the reader can act on them. ✅ "Warp tracks every run. Check its status from the CLI, the API, or the dashboard." ❌ "The orchestration layer runs on Warp's servers (cloud control plane) and tracks lifecycle state (created → running → completed/failed)."
- **Descriptive link text, and no dead-end pages** — Never use "here", "this page", or a bare URL as link text. End every new page with a `## Related pages` section (or the type-equivalent, such as `## Next steps` on a quickstart) containing at least one internal link whose anchor names the destination topic. ✅ `Learn more about [Codebase Context](/code/codebase-context/)` ❌ `Click [here](/code/codebase-context/)` ❌ ending a new feature page with no cross-links
- **Disambiguate conditional and multi-clause wording** — If a sentence has two plausible readings (especially with "when", "if", "can", or stacked clauses), rewrite it so only one meaning remains. Prefer one idea per sentence. ✅ `Cloud handoff keeps your conversation's model only when that model is available in the cloud.` ❌ `Cloud handoff keeps your conversation's model when it can run in the cloud.` (keeps the model when it can? or only when cloud supports the model?)
- **Lead instructional sentences with the action or goal** — In steps, keyboard shortcuts, and "how to" sentences, put the action or goal first, then the control or condition. Readers should not need prior context to know what values or targets you mean. ✅ `To open the searchable environment and model selectors, press Ctrl+E.` ❌ `To change either value, press Ctrl+E.` (which values?)
Expand Down Expand Up @@ -143,7 +145,10 @@ Before presenting the draft, verify against the quality checklist in `AGENTS.md`
- [ ] Content follows the structure for its content type
- [ ] Section order follows reader chronology (requirements → setup → usage → advanced → troubleshooting)
- [ ] Error messages and failure modes live in Troubleshooting, not woven through the main flow
- [ ] Callouts are sparse (usually 0–2 per page) and not used as a substitute for body prose
- [ ] Callouts are sparse (usually 0–2 per page), never consecutive, and not used as a substitute for body prose
- [ ] Prose passes the tone rules: no marketing buzzwords, no meta-openers ("This page covers..."), no restated cause-and-effect or recap lines, and it reads naturally aloud (AGENTS.md → Voice & tone)
- [ ] Internal architecture (orchestrators, control planes, lifecycle states) appears only where the reader can act on it, and relocated detail landed on a maintainer-facing surface instead of being deleted
- [ ] A deletion-only second pass removed framing lines, self-commentary, rule justifications, and boilerplate a parent page already covers (AGENTS.md → Voice & tone → Cut again)
- [ ] Terminology matches the glossary (`.agents/references/terminology.md`)
- [ ] Headers use sentence case (with proper feature name capitalization)
- [ ] Headers name a specific topic (not bare Overview / More details / Other)
Expand Down
3 changes: 2 additions & 1 deletion .agents/skills/review-docs-pr/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The 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 leverage and misses a 2,000-word page that should be 600 — the more expensive problem, and the one we're actually seeing.

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.

Expand Down
10 changes: 10 additions & 0 deletions .agents/skills/style_lint/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,11 +63,21 @@ python3 .agents/skills/style_lint/style_lint.py --all --fix --create-pr
- **Warp Factories naming**: A bare capitalized "Factory" used as a proper noun. "Warp Factories" is the product and is written in full; an individual "factory" is lowercase. Sentence-, heading-, bullet-, quote-, and cell-initial capitals are positional and stay, as do frontmatter titles and labels, the shipped feature name "Factory MCP", and verbatim UI strings such as **Factory name** and **Add your Factory to your team**. Regression cases live in `test_factory_proper_noun.py`.
- **Hardcoded product name strings**: Product name strings that have a corresponding key in `src/data/vars.ts` but appear as literal text rather than variable syntax. Reports instances of known strings like "Oz CLI", "Oz web app", "oz.warp.dev", "Oz dashboard", "Oz run" (any value currently in `src/data/vars.ts`) in body prose and frontmatter. These are flagged as `⚠️ [IMPORTANT]` in PR context and reported (not auto-fixed) — they should use `{VARS.KEY}` in prose and `{{TOKEN}}` in frontmatter.

### Tone checks (report-only, never auto-fixed)

These enforce the "Voice & tone" section of `AGENTS.md`. Every hit needs a human rewrite, so they are always warnings and never auto-fixed:

- **Buzzwords** (`tone-buzzword`): AI-ism words like "seamless", "powerful", "robust", "comprehensive", "leverage", "streamline", "empower", "delve", and filler frames like "it's important to note". Words with legitimate technical uses in these docs ("harness", "unlock", "elevated", "journey") are deliberately excluded from the lint and covered by prose guidance only.
- **Meta-openers** (`tone-meta-opener`): Page-narrating text like "This page covers/explains/walks through...". The fix is to cut the sentence and state the thing itself.
- **Consecutive callouts** (`callout-consecutive`): Two `:::` asides back to back with nothing between them. Merge them or move one into body prose.
- **Callout budget** (`callout-density`): More than 4 callouts on one page. The style guide allows at most one per section; the per-page count is the lintable proxy.

## Auto-fix behavior

When run with `--fix`:
- **High-confidence fixes applied automatically**: Settings path format, UI element format, product name casing, external product name casing
- **Low-confidence issues reported but not auto-fixed**: link quality, VideoEmbed title specificity, list format, header case (due to feature name exceptions), ambiguous terminology
- **Tone checks are never auto-fixed**: buzzwords, meta-openers, and callout budget issues always need a human rewrite

## Relationship to validate_ui_refs

Expand Down
148 changes: 148 additions & 0 deletions .agents/skills/style_lint/style_lint.py
Original file line number Diff line number Diff line change
Expand Up @@ -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]] = [

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The lint covers 2 of the 6 filler frames in AGENTS.md. Missing: designed to, ensures that, allows you to, in order to. Also missing landscape and realm from Abstract dramatics.

in order toto and allows you to are the highest-frequency AI tells here and are trivially matchable.

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)"

Expand Down Expand Up @@ -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:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The three new checks ship without tests. style_lint/ already has test_factory_proper_noun.py, test_hardcoded_var_exemptions.py, and test_platform_determiner.py — and the SKILL.md edited in this PR says "Regression cases live in test_factory_proper_noun.py."

Regexes over prose are exactly where false positives creep in. A test_tone_checks.py with the intentional exclusions as negative cases would lock in the judgment calls you already made.

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

Expand All @@ -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)))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ [IMPORTANT] These new lint checks are enabled for every scan, but the diff does not add regression tests for their matching and exclusion behavior. Existing style_lint rules have focused test_*.py coverage; add cases for buzzwords, meta-openers, inline/code-block skipping, consecutive callouts, and density before wiring these into run_all_checks.

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)))
Expand Down
Loading
Loading