Skip to content
Open
18 changes: 18 additions & 0 deletions .agents/references/terminology.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,9 @@ For the summary of the most critical terms (core features, Automation Platform t
- **Tab Configs** — Reusable TOML-based tab layout definitions that launch preconfigured terminal sessions.
*Usage note:* Capitalize as a feature name.

- **Launch Configuration** / **Launch Configurations** — The legacy, YAML-based way to save a configuration of windows, tabs, and panes. Replaced by Tab Configs; existing Launch Configurations still work but aren't being extended.
*Usage note:* Capitalize as a feature name, including in headings ("Creating a Launch Configuration"). Use "Tab Configs" for new content.

- **Vertical Tabs** — The sidebar-based tab management panel that replaces the horizontal tab bar.
*Usage note:* Capitalize as a feature name.

Expand Down Expand Up @@ -108,6 +111,12 @@ For the summary of the most critical terms (core features, Automation Platform t
- **Active AI** — The feature that proactively surfaces fixes and next actions based on terminal errors, inputs, and outputs. Covers Prompt Suggestions, Next Command predictions, and Suggested Code Diffs.
*Usage note:* Always capitalize "Active AI" — it is a proper feature name and the label of the Settings toggle. Write the full capability as **Active AI Recommendations**, matching the page title. Lowercase the following word only when it is a plain common noun rather than part of the name, as in "Active AI features" or "the Active AI toggle".

- **Computer Use** — The capability that lets agents interact with a desktop environment (screenshots, clicks, typing) inside a sandboxed cloud environment.
*Usage note:* Capitalize as a feature name, including in headings ("Enabling Computer Use", "How Computer Use works").

- **Full Terminal Use** — The capability that lets an agent attach to and drive interactive terminal programs (for example `psql`, `vim`, a dev server) by reading live PTY output.
*Usage note:* Capitalize as a feature name, including in headings.

## Coding terms (Warp features)

- **Code** — Warp's coding experience for agent-assisted changes (editing, diffs, code review).
Expand Down Expand Up @@ -275,6 +284,9 @@ Not every "Oz" in the docs is stale. These are deliberate and correct until
- **Automation Platform** — Warp's cloud agent platform, covering environments, integrations, orchestration, self-hosting, and the Agent API/SDK. Renamed from "Oz" on 2026-08-18.
*Usage note:* See "Automation Platform terminology" above for the full entry and the article rule. Always write it as `{VARS.WARP_AUTOMATION_PLATFORM}` / `{{WARP_AUTOMATION_PLATFORM}}`, never as a literal string, so a future rename stays a one-line change.

- **Direct backend** — The self-hosted worker backend that runs cloud agent tasks directly on the worker host, without Docker or Kubernetes. One of three self-hosting backends alongside the Docker and Kubernetes backends.
*Usage note:* Capitalize "Direct" as part of the compound proper term, including in headings ("Start the worker with the Direct backend"); lowercase "backend" as the common noun.

## Warp Factories terminology

### The product/instance rule
Expand Down Expand Up @@ -344,6 +356,9 @@ Docs match the screen; the fix belongs in the app.
- **command-line** — hyphenated when used as an adjective
- **Git repository** or **repo** — not "git repository" (capitalize "Git")
- **macOS** — not "Mac OS" or "Mac"
- **Zero Data Retention (ZDR)** — Warp's agreements with contracted LLM providers not to train on or retain customer data. Capitalize as a proper term, including in headings ("Zero Data Retention (ZDR)"); keep the "(ZDR)" acronym on first mention in a section.
- **SSO (Single Sign-On)** — Authentication through an organization's identity provider (Okta, Microsoft Entra ID, Google Workspace, or any SAML 2.0/OIDC provider). Always capitalize the acronym "SSO"; capitalize "Single Sign-On" when spelled out as the feature name.
- **Bring Your Own LLM (BYOLLM)** — Warp Enterprise's routing of agent inference through a customer's own cloud infrastructure (AWS Bedrock or Google Cloud via Gemini Enterprise). Capitalize as a proper feature name, including in headings; keep the "(BYOLLM)" acronym on first mention in a section. Distinct from BYOK (Bring Your Own API Key), a self-serve, user-level feature.

## Branded and informal terms

Expand Down Expand Up @@ -387,3 +402,6 @@ Docs match the screen; the fix belongs in the app.
- **GitHub App** — GitHub's installation/auth mechanism used for repo access in integrations
- **Linear** — capitalize
- **Slack** — capitalize
- **Bitbucket Data Center** — Atlassian's official self-hosted Bitbucket edition name (alongside Bitbucket Server and Bitbucket Cloud). Capitalize all three words, including in headings ("Bitbucket Data Center / Server").
- **Workload Identity Pool and Provider** — GCP's IAM resources for federating external identities. Capitalize as GCP's official term, including in headings.
- **Workload Identity Federation** — GCP's mechanism for granting external identities access without a long-lived service account key. Capitalize as GCP's official term, including in headings.
8 changes: 3 additions & 5 deletions .agents/skills/missing_docs/references/feature_surface_map.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ CodeFindReplace -> src/content/docs/code/code-editor/find-and-replace.md
VimCodeEditor -> src/content/docs/code/code-editor/code-editor-vim-keybindings.md
McpServer -> src/content/docs/agents/capabilities/mcp.mdx
McpOauth -> src/content/docs/agents/capabilities/mcp.mdx
# Factory MCP (the hosted MCP endpoint agents use to work with a factory) is
# GA and documented on the Factory MCP page and related factories/* pages.
FactoryMcp -> src/content/docs/factories/factory-mcp.mdx
ImageAsContext -> src/content/docs/agents/local-agents/agent-context/images-as-context.mdx
SelectionAsContext -> src/content/docs/agents/local-agents/agent-context/selection-as-context.mdx
DiffSetAsContext -> src/content/docs/agents/local-agents/agent-context/selection-as-context.mdx
Expand Down Expand Up @@ -242,11 +245,6 @@ OscHyperlinks -> src/content/docs/terminal/more-features/files-and-links.mdx
# MCP reference (and the cloud agent MCP schema page).
WellKnownMcpIds -> src/content/docs/reference/cli/mcp-servers.mdx

# Auto-attaches the Warp-hosted Factory MCP server to agent sessions with no
# manual setup. Promoted dogfood -> GA; that zero-config behavior is documented
# in the "Connect and authenticate" section of the Factory MCP page.
FactoryMcp -> src/content/docs/factories/factory-mcp.mdx

## CLI commands -> doc pages

# Top-level Oz CLI commands
Expand Down
11 changes: 8 additions & 3 deletions .agents/skills/style_lint/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,14 +85,19 @@ This skill checks broader formatting and terminology. The `validate_ui_refs` ski

## Tests

Three checks have regression suites, because each is a narrow rule where the
hard part is not firing on legitimate text. Run them after touching any of
these checks:
Several checks have regression suites, because each is a narrow rule where the
hard part is not firing on legitimate text. Run the relevant one(s) after
touching the corresponding check:

```bash
python3 .agents/skills/style_lint/test_platform_determiner.py
python3 .agents/skills/style_lint/test_factory_proper_noun.py
python3 .agents/skills/style_lint/test_hardcoded_var_exemptions.py
python3 .agents/skills/style_lint/test_header_case_sentence_boundary.py
python3 .agents/skills/style_lint/test_product_casing_word_boundary.py
python3 .agents/skills/style_lint/test_proper_feature_names_third_party.py
python3 .agents/skills/style_lint/test_tone_checks.py
python3 .agents/skills/style_lint/test_context_aware_terminology.py
```

## Dependencies
Expand Down
144 changes: 124 additions & 20 deletions .agents/skills/style_lint/style_lint.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,16 @@
"Oz CLI", "Oz Platform", "Project Rules",
"Slash Commands", "Terminal Mode", "Universal Input", "Warp Drive",
"Warp Platform", "Automation Platform", "Warp Factories", "Factory MCP",
"Computer Use", "Full Terminal Use", "Zero Data Retention", "Single Sign-On",
"Blocks", "Block", "Tab Configs", "Tab Config",
"Launch Configuration", "Launch Configurations",
# Warp's own feature name.
"Bring Your Own LLM",
# Third-party/technical proper nouns. style_lint's proper-noun protection
# is scoped to Warp's own product names by design; these are narrow,
# named exceptions rather than a generalization of that scope.
"Bitbucket Data Center", "Workload Identity Pool and Provider",
"Workload Identity Federation", "Direct backend",
}

# Terminology: wrong → right (case-sensitive checks)
Expand Down Expand Up @@ -448,6 +458,22 @@ def _normalize_link_text(text: str) -> str:
return text.strip(" \t\n\r.,:;!?()[]{}\"'")


def _strip_markdown_destinations(line: str) -> str:
"""Remove Markdown image/link destination text, keeping the associated
human-visible alt/link text in place.

A destination is a literal filename or URL (e.g. the
"Blocklist-with-review-changes.png" in
`![Review changes...](../../assets/Blocklist-with-review-changes.png)`),
not prose -- casing and deprecated-term checks must not fire on it. The
`![alt](...)` / `[text](...)` syntax is identical for images and links,
so `](...)` -> `]` strips the destination from both while leaving the
bracketed text (and the leading `!` for images) for the surrounding
prose checks to scan normally.
"""
return re.sub(r"\]\([^)]*\)", "]", line)


def _meaningful_words(text: str) -> List[str]:
"""Return lowercase words that carry semantic meaning for comparisons."""
stopwords = {"a", "an", "and", "for", "in", "of", "on", "the", "to", "with", "x"}
Expand Down Expand Up @@ -569,6 +595,24 @@ def check_ui_element_backticks(lines: List[str], filepath: str) -> List[Issue]:
return issues


# Marks the end of a rendered sentence within a single header/line: a
# `.`/`!`/`?`, optionally followed by a closing quote or bracket. A header can
# legitimately contain more than one sentence (e.g. a two-question FAQ title
# or a comment-hidden boundary), and only the very first word of *each*
# sentence is sentence-initial -- not just the first word of the whole line.
_SENTENCE_END_RE = re.compile(r'[.!?]["\')\]]*$')


def _sentence_start_indices(words: List[str]) -> set:
"""Return word indices that start a sentence: index 0, plus any word
immediately following another word ending in `.`, `!`, or `?`."""
starts = {0}
for idx in range(1, len(words)):
if _SENTENCE_END_RE.search(words[idx - 1]):
starts.add(idx)
return starts


def _to_sentence_case(text: str) -> str:
"""Convert header text to sentence case, preserving proper feature names and acronyms."""
skip_words = {"I", "A", "API", "CLI", "SDK", "SSH", "UI", "URL", "PR", "CI", "CD"}
Expand All @@ -584,9 +628,11 @@ def _to_sentence_case(text: str) -> str:
for j in range(len(fn_words)):
protected[start + j] = True

sentence_starts = _sentence_start_indices(words)

result = []
for idx, w in enumerate(words):
if idx == 0 or protected[idx]:
if idx in sentence_starts or protected[idx]:
result.append(w)
continue
clean = re.sub(r"[^a-zA-Z]", "", w)
Expand Down Expand Up @@ -615,10 +661,16 @@ def check_header_case(lines: List[str], filepath: str) -> List[Issue]:
words = text.split()
if len(words) < 2:
continue
# Count capitalized non-first words (excluding proper feature names, short words)
# Count capitalized non-first words (excluding proper feature names,
# short words, and the first word of any later sentence in the same
# header -- a header can contain more than one sentence, e.g. a
# two-question FAQ title, and each one gets its own capitalized start).
skip_words = {"I", "A", "API", "CLI", "SDK", "SSH", "UI", "URL", "PR", "CI", "CD"}
sentence_starts = _sentence_start_indices(words)
title_case_count = 0
for w in words[1:]:
for idx, w in enumerate(words):
if idx in sentence_starts:
continue
clean = re.sub(r"[^a-zA-Z]", "", w)
if not clean or clean in skip_words or len(clean) <= 2:
continue
Expand Down Expand Up @@ -857,33 +909,68 @@ def check_callout_syntax(lines: List[str], filepath: str) -> List[Issue]:
return issues


def _word_bounded_pattern(term: str) -> re.Pattern:
"""Compile a case-sensitive pattern that only matches `term` as a whole
word/phrase, not as a substring of a longer token.

A plain `str.find` (the previous implementation) matched "agent mode"
inside "agent model", since "agent mode" is literally a substring of
"agent model" -- and corrupted it into "Agent Model" on `--fix`. A plain
`\\b...\\b` regex doesn't fully fix this either: `\\b` requires a
transition to/from a word character, which fails for terms ending in
punctuation (e.g. "A.I." followed by a space or end of line has no such
transition). Lookarounds that only check the adjacent character is not a
word character work for both cases regardless of the term's own leading
or trailing characters.
"""
return re.compile(r"(?<!\w)" + re.escape(term) + r"(?!\w)")


_PRODUCT_CASING_PATTERNS = [
(_word_bounded_pattern(wrong), wrong, right, note)
for wrong, (right, note) in PRODUCT_CASING.items()
]
_EXTERNAL_CASING_PATTERNS = [
(_word_bounded_pattern(wrong), wrong, right, note)
for wrong, (right, note) in EXTERNAL_CASING.items()
]


def check_product_casing(lines: List[str], filepath: str) -> List[Issue]:
"""Check for incorrect product name casing."""
"""Check for incorrect product name casing.

Tracks complete fenced code blocks (not just a line that happens to start
with a backtick) so a wrong-cased term inside a multi-line code fence --
e.g. a literal `/Applications/Warp.app/Contents/MacOS/stable` path in a
shell snippet -- is left alone as executable/literal text. Markdown
image/link destinations are stripped for the same reason: a filename in a
link target is not prose, even though the line itself is not code.
"""
issues = []
in_code_block = False
for i, line in enumerate(lines, 1):
# Skip code blocks
if line.strip().startswith("```") or line.strip().startswith("`"):
stripped = line.strip()
if stripped.startswith("```"):
in_code_block = not in_code_block
continue
if in_code_block or stripped.startswith("`"):
continue
for wrong, (right, note) in PRODUCT_CASING.items():
# Case-sensitive search
idx = line.find(wrong)
while idx != -1:
prose_line = _strip_markdown_destinations(line)
for pattern, wrong, right, note in _PRODUCT_CASING_PATTERNS:
for _ in pattern.finditer(prose_line):
issues.append(Issue(
filepath, i, "product-casing",
f"\"{wrong}\" → \"{right}\" ({note})",
"warning", fixable=True, fix_from=wrong, fix_to=right,
))
idx = line.find(wrong, idx + len(wrong))

for wrong, (right, note) in EXTERNAL_CASING.items():
idx = line.find(wrong)
while idx != -1:
for pattern, wrong, right, note in _EXTERNAL_CASING_PATTERNS:
for _ in pattern.finditer(prose_line):
issues.append(Issue(
filepath, i, "external-casing",
f"\"{wrong}\" → \"{right}\" ({note})",
"warning", fixable=True, fix_from=wrong, fix_to=right,
))
idx = line.find(wrong, idx + len(wrong))
return issues


Expand Down Expand Up @@ -914,13 +1001,25 @@ def check_oz_terms(lines: List[str], filepath: str) -> List[Issue]:


def check_deprecated_terms(lines: List[str], filepath: str) -> List[Issue]:
"""Check for deprecated terminology (whitelist/blacklist/blocklist)."""
"""Check for deprecated terminology (whitelist/blacklist/blocklist).

Tracks complete fenced code blocks (see check_product_casing) and strips
Markdown image/link destinations so a literal filename like
"Blocklist-with-review-changes.png" in a link target is not flagged as
prose using the deprecated term.
"""
issues = []
in_code_block = False
for i, line in enumerate(lines, 1):
if line.strip().startswith("```") or line.strip().startswith("`"):
stripped = line.strip()
if stripped.startswith("```"):
in_code_block = not in_code_block
continue
if in_code_block or stripped.startswith("`"):
continue
prose_line = _strip_markdown_destinations(line)
for pattern, suggestion in DEPRECATED_TERMS:
for m in re.finditer(pattern, line, re.IGNORECASE):
for m in re.finditer(pattern, prose_line, re.IGNORECASE):
issues.append(Issue(
filepath, i, "deprecated-term",
f"Avoid \"{m.group(0)}\" → {suggestion}",
Expand Down Expand Up @@ -1296,11 +1395,16 @@ def check_factory_proper_noun(lines: List[str], filepath: str) -> List[Issue]:
continue
if any(phrase in line for phrase in FACTORY_ALLOWED_PHRASES):
continue
# Strip inline code, link targets, and HTML/JSX attributes: a slug like
# `/factories/factory-as-code/` or an `alt="..."` value is not prose.
# Strip inline code, link targets, HTML/JSX attributes, and JSX/MDX
# comments: a slug like `/factories/factory-as-code/` or an
# `alt="..."` value is not prose, and a `{/* ... */}` comment renders
# to nothing, so the text right after one is not mid-sentence just
# because the raw source has no space/punctuation there -- the actual
# sentence boundary can be hidden inside the stripped comment.
prose = re.sub(r"`[^`]*`", "", line)
prose = re.sub(r"\]\([^)]*\)", "]", prose)
prose = re.sub(r'\w+="[^"]*"', "", prose)
prose = re.sub(r"\{/\*.*?\*/\}", "", prose)
for m in FACTORY_BARE.finditer(prose):
before = prose[:m.start()]
after = prose[m.end():]
Expand Down
Loading
Loading