write-product-spec

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

write-product-spec

编写PRODUCT.md规格文档

Write a
PRODUCT.md
spec for a significant feature in Warp.
为Warp中的重要功能编写
PRODUCT.md
规格文档。

Overview

概述

The product spec should make the desired behavior unambiguous enough that an agent can implement it correctly and avoid regressions. Describe the feature purely from the user's perspective — what the user sees, does, and experiences, and the invariants that must hold for them. Do not include implementation details (internal types, state layout, module boundaries, data flow, algorithms).
"User" is not limited to the end user of the Warp app. It means whoever consumes the surface being designed:
  • For UI / UX features: the human using Warp.
  • For a data model: the code that reads and writes that model.
  • For an API, protocol, or library: the callers of that API — other services, client code, plugins, or agents.
  • For a CLI tool or developer-facing surface: the developer invoking it.
The spec should describe behavior from that consumer's perspective: the shape of the surface, the operations they can perform, what they see back, invariants they can rely on, and edge cases they must handle — without prescribing how the surface is implemented underneath.
Implementation details, validation, and test planning live in a companion
TECH.md
, produced by the
write-tech-spec
skill. Writing the product spec is usually the first step of a two-step process: once
PRODUCT.md
is agreed on, invoke
write-tech-spec
to produce
TECH.md
for the same feature (or let the user know that's the expected next step). The product spec should be written so the tech spec can be written directly from it.
Write specs to
specs/<id>/PRODUCT.md
, where
<id>
is one of:
  • a Linear ticket number (e.g.
    specs/APP-1234/PRODUCT.md
    )
  • a GitHub issue id, prefixed with
    gh-
    (e.g.
    specs/gh-4567/PRODUCT.md
    )
  • a short kebab-case feature name (e.g.
    specs/vertical-tabs-hover-sidecar/PRODUCT.md
    )
specs/
should contain only id-named directories as direct children — no engineer-named subdirectories.
Ticket / issue references are optional. If the user has a Linear ticket or GitHub issue, use its id. If they don't, ask them for a feature name to use as the directory. Only create a new Linear ticket or GitHub issue when the user explicitly asks for one; in that case use the Linear MCP tools or
gh
CLI respectively (and
ask_user_question
if team, labels, or repo are unclear).
产品规格文档应明确期望行为,确保Agent能正确实现功能并避免回归问题。需完全从用户视角描述功能——用户看到的内容、执行的操作、获得的体验,以及必须遵循的不变规则。请勿包含实现细节(内部类型、状态结构、模块边界、数据流、算法)。
此处的“用户”不仅限于Warp应用的终端用户,而是指所有使用该设计界面的角色:
  • 对于UI/UX功能:使用Warp的终端用户。
  • 对于数据模型:读写该模型的代码。
  • 对于API、协议或库:调用该API的对象——其他服务、客户端代码、插件或Agent。
  • 对于CLI工具或面向开发者的界面:调用该工具的开发者。
规格文档应从消费者视角描述行为:界面的形态、可执行的操作、返回的内容、可依赖的不变规则、必须处理的边缘情况——无需规定界面底层的实现方式。
实现细节、验证与测试规划需放在配套的
TECH.md
文档中,由
write-tech-spec
技能生成。编写产品规格文档通常分为两步:
PRODUCT.md
达成共识后,调用
write-tech-spec
为同一功能生成
TECH.md
(或告知用户这是预期的下一步)。产品规格文档的编写需确保技术规格文档可直接基于它生成。
规格文档需写入
specs/<id>/PRODUCT.md
,其中
<id>
可选以下类型:
  • Linear工单编号(例如:
    specs/APP-1234/PRODUCT.md
  • GitHub issue编号,前缀为
    gh-
    (例如:
    specs/gh-4567/PRODUCT.md
  • 简短的短横线命名风格的功能名称(例如:
    specs/vertical-tabs-hover-sidecar/PRODUCT.md
specs/
目录下仅允许直接存放以id命名的子目录——不允许存放以工程师命名的子目录。
工单/issue引用为可选内容。若用户提供Linear工单或GitHub issue,使用其编号;若未提供,则向用户询问功能名称作为目录名。仅当用户明确要求时,才创建新的Linear工单或GitHub issue;此时需分别使用Linear MCP工具或
gh
CLI(若团队、标签或仓库信息不明确,使用
ask_user_question
询问)。

Before writing

编写前准备

Gather only the context you need: directory id (Linear ticket, GitHub issue, or feature name), feature summary, target users, key behaviors, edge cases, and how the feature will be validated. Use
ask_user_question
for missing context rather than guessing.
仅收集所需的上下文信息:目录id(Linear工单、GitHub issue或功能名称)、功能概述、目标用户、核心行为、边缘情况,以及功能的验证方式。若信息缺失,使用
ask_user_question
询问用户,而非自行猜测。

Figma mocks

Figma原型

If the feature has any UI or interaction design, ask the user whether a Figma mock exists before drafting the Behavior section, and include the link in the spec when one is provided. A mock is often the most reliable source of truth for visual states, spacing, and edge-case layouts — not asking can cause the Behavior section to guess at intent the designer already settled.
  • If the user provides a link, include it under a short
    ## Figma
    section (or inline near the top of Behavior) as
    Figma: <link>
    .
  • If the user confirms no mock exists, note
    Figma: none provided
    so the absence is explicit rather than ambiguous.
  • If the feature is purely backend (data model, API, CLI with no visual surface), skip the question and omit the section.
Do not silently drop design context; an explicit "none" is preferable to no mention at all on features where design would normally be expected.
若功能涉及UI或交互设计,在起草“行为”章节前,询问用户是否存在Figma原型,并在提供链接时将其加入规格文档。原型通常是视觉状态、间距和边缘情况布局最可靠的依据——若未询问,可能导致“行为”章节对设计师已确定的意图做出错误猜测。
  • 若用户提供链接,在简短的
    ## Figma
    章节(或“行为”章节顶部附近)添加
    Figma: <链接>
  • 若用户确认无原型,标注
    Figma: none provided
    ,明确说明未提供原型,避免歧义。
  • 若功能纯为后端(数据模型、API、无视觉界面的CLI),跳过此问题并省略该章节。
请勿忽略设计上下文;对于通常需要设计的功能,明确标注“无”比完全不提更合适。

Structure

文档结构

Required sections:
  1. Summary — 1–3 sentences describing the feature and desired outcome.
  2. Behavior — The meat of the spec. An exhaustive English description of how the feature works, written as numbered, testable invariants. See "The Behavior section" below — this is where the spec earns its length, and everything else should stay thin to avoid duplicating it.
Optional sections — include only when they add signal beyond the core. Omit the heading entirely if empty; do not write "None" as a placeholder.
  • Problem — Include only when the motivation isn't obvious from Summary.
  • Goals / Non-goals — Include when scope is ambiguous or has been contested.
  • Figma — Include with a link when one exists, or an explicit
    Figma: none provided
    note when design matters but no mock exists. Omit entirely for non-visual features. See "Figma mocks" above.
  • Open questions — Prefer inline
    **Open question:** …
    next to the relevant behavior. Include a dedicated section only if there are multiple unresolved questions worth collecting.
Do not include Validation, Success criteria, or Testing sections. Validation and test planning live in the companion
TECH.md
(produced by
write-tech-spec
). Write Behavior as numbered invariants that are testable on their own — the tech spec can reference them directly.
必填章节:
  1. 概述——1-3句话描述功能及预期成果。
  2. 行为——规格文档的核心内容。以详尽的描述说明功能工作方式,写成可测试的编号不变规则。详见下文“行为章节”——这是规格文档的核心,其他部分应尽量精简,避免重复。
可选章节——仅当能提供核心内容之外的有效信息时才包含。若为空,完全省略标题;请勿写入“无”作为占位符。
  • 问题背景——仅当动机无法从“概述”中明确体现时包含。
  • 目标/非目标——当范围存在歧义或存在争议时包含。
  • Figma——若存在原型链接则包含,或当设计重要但无原型时标注
    Figma: none provided
    。非视觉功能完全省略该章节。详见上文“Figma原型”。
  • 待解决问题——优先在相关行为旁添加内联的
    **Open question:** …
    。仅当存在多个值得汇总的未解决问题时,才设置专门章节。
请勿包含验证、成功标准或测试章节。验证与测试规划需放在配套的
TECH.md
文档中(由
write-tech-spec
生成)。将“行为”写成可独立测试的编号不变规则——技术规格文档可直接引用这些规则。

The Behavior section

行为章节

Behavior is the spec. Everything else is framing.
The goal of Behavior is a complete English description of how the feature works, detailed enough that a tech spec can be written directly from it without the author having to guess or re-derive product intent. If a reader finishes Behavior with questions about what the feature does in some situation, the section is not done.
Describe, at minimum:
  • Default behavior and the happy-path user flow.
  • Every user-visible state and the transitions between them.
  • All inputs the user can provide and how the feature responds.
  • Empty states, error states, loading / pending states, and cancellation.
  • Edge cases a reasonable implementer would not think to ask about — permission denied, offline, timeouts, races between state changes, multiple concurrent instances, stale or missing data, focus loss mid-interaction, interactions with adjacent features.
  • Keyboard, accessibility, and focus expectations where relevant.
  • Invariants that must hold at all times and behaviors that must not regress.
Length Behavior to match the feature. Trivial features may need a handful of invariants; complex features may need many, with sub-sections per flow or state. The rest of the spec should stay thin so Behavior can be as exhaustive as the feature requires without producing a bloated document overall. Err toward enumerating one more edge case rather than one fewer.
行为部分就是规格文档的核心,其他部分仅为框架说明。
行为章节的目标是完整描述功能的工作方式,详细到无需作者猜测或重新推导产品意图即可直接编写技术规格文档。若读者读完行为章节后仍对某些场景下的功能行为存在疑问,说明该章节未完成。
至少需描述:
  • 默认行为与用户操作的顺畅路径。
  • 所有用户可见的状态及状态间的转换。
  • 用户可提供的所有输入及功能的响应方式。
  • 空状态、错误状态、加载/待处理状态及取消操作。
  • 合理的实现者可能不会想到的边缘情况——权限拒绝、离线、超时、状态变更竞争、多个并发实例、过期或缺失数据、交互过程中失去焦点、与相邻功能的交互。
  • 相关的键盘操作、无障碍访问及焦点预期。
  • 必须始终遵循的不变规则及不得回归的行为。
行为章节的长度需与功能规模匹配。简单功能可能只需几条不变规则;复杂功能可能需要多条规则,并按流程或状态划分小节。文档的其他部分应尽量精简,以便行为章节可根据功能需求尽可能详尽,同时避免文档整体过于臃肿。宁可多列举一个边缘情况,也不要遗漏。

Length heuristic

长度参考

Behavior should be as long as the feature requires — do not truncate edge cases to hit a line target. The heuristic below applies to everything around Behavior (Summary, optional sections): keep that framing thin so the spec's total length reflects the feature's actual complexity, not structural overhead.
  • Trivial fix or narrow UI tweak: no spec.
  • Small feature (single module, few edge cases): framing plus Behavior typically ~30–60 lines total.
  • Medium feature (cross-module, multiple states): typically ~80–150 lines total.
  • Large or behaviorally rich feature: longer is fine, and most of the length should live in Behavior.
If you find yourself writing the same idea in Summary, Problem, Goals, and Behavior, collapse the framing — not the Behavior content.
行为章节的长度应与功能需求相符——不要为了达到行数目标而删减边缘情况。以下参考适用于行为章节之外的所有内容(概述、可选章节):保持框架内容精简,使文档总长度反映功能的实际复杂度,而非结构开销。
  • 微小修复或局部UI调整:无需编写规格文档。
  • 小型功能(单一模块,少量边缘情况):框架内容加行为章节总长度约30-60行。
  • 中型功能(跨模块,多状态):总长度约80-150行。
  • 大型或行为丰富的功能:长度可更长,且大部分内容应集中在行为章节。
若发现自己在概述、问题背景、目标和行为章节中重复表达相同观点,应精简框架内容——而非删减行为章节的内容。

Writing guidance

编写指南

  • Prefer concrete, observable behavior over aspirational wording.
  • Write Behavior as a list of invariants rather than prose when possible.
  • Capture invariants that must not regress and edge cases that are easy to miss.
  • Avoid implementation details unless unavoidable for the UX.
  • Each section should earn its place — if a section would repeat another or contain only boilerplate, omit it.
  • 优先使用具体、可观察的行为描述,而非理想化措辞。
  • 尽可能将行为章节写成不变规则列表,而非散文。
  • 记录不得回归的不变规则及容易遗漏的边缘情况。
  • 避免包含实现细节,除非对用户体验不可避免。
  • 每个章节都应有存在的价值——若章节会重复其他内容或仅包含模板内容,应省略。

Keep the spec current

保持规格文档更新

Approved specs may ship in the same PR as the implementation. As implementation evolves, update
PRODUCT.md
in the same PR when user-facing behavior or UX details change. The checked-in spec should describe the feature that actually ships.
For large features, the implementer may optionally keep a
DECISIONS.md
file summarizing concrete decisions made during design and implementation. Offer it when it would help future agents; otherwise skip it.
已批准的规格文档可与实现代码合并到同一个PR中。随着实现推进,当用户面向的行为或UX细节发生变化时,需在同一个PR中更新
PRODUCT.md
。已提交的规格文档应描述实际发布的功能。
对于大型功能,实现者可选择保留
DECISIONS.md
文件,总结设计与实现过程中做出的具体决策。若有助于未来的Agent,可提供该文件;否则可省略。

Related Skills

相关技能

  • implement-specs
  • write-tech-spec
  • spec-driven-implementation
  • implement-specs
  • write-tech-spec
  • spec-driven-implementation

Example Behavior section

示例行为章节

A sample Behavior section for a hypothetical feature: rendering GitHub-flavored Markdown tables in the Warp block list. It demonstrates the expected shape — numbered, testable, user-perspective invariants that enumerate defaults, edge cases, malformed input, streaming, selection/copy, search, sharing, theming, and cross-surface consistency, with one inline open question.
markdown
undefined
以下是一个假设功能的行为章节示例:在Warp块列表中渲染GitHub风格的Markdown表格。该示例展示了预期的结构——编号、可测试、从用户视角出发的不变规则,涵盖了默认行为、边缘情况、格式错误的输入、流式输出、选择/复制、搜索、分享、主题设置及跨界面一致性,并包含一个内联待解决问题。
markdown
undefined

Behavior

行为

  1. When a terminal output block contains a GitHub-flavored Markdown table (a header row, a separator row of one or more
    ---
    segments, and one or more body rows, all delimited by
    |
    ), that table renders as a visually formatted table in the block — not as raw pipe-delimited text.
  2. The table renders with:
    • A visually distinct header row.
    • Aligned columns based on the separator row:
      |:---|
      left-align,
      |:---:|
      center,
      |---:|
      right-align.
      |---|
      with no colons falls back to the default alignment (left for text, right for numeric-looking values).
    • Visible row separators (or equivalent spacing) consistent with the active theme.
  3. Inline markdown inside a cell renders inline: bold, italic, inline code, strikethrough, and links all render the same way they do in the surrounding block output. Line breaks inside a cell (
    <br>
    or escaped
    \n
    ) render as in-cell line breaks.
  4. Column widths are chosen to fit the table's natural content when it fits inside the block. If a single cell's content is very long, that cell wraps its text within its column rather than forcing the column to an unreasonable width.
    • Open question: when a wrapped cell would produce an unreasonably tall row, do we clip with an "expand" affordance, or let the row grow unbounded?
  5. Horizontal scrolling: when the table's total width exceeds the block width — many columns, or wide columns that can't reasonably be narrowed — the table becomes horizontally scrollable within the block. Scrolling horizontally reveals off-screen columns without clipping or truncating them. Vertical scrolling of the block continues to work independently of table scroll.
  6. When the block is resized (terminal resize, pane split, sidebar open/close), the table reflows to the new width without losing row or column order.
  7. Empty cells render as visibly empty (same row height as surrounding cells, no placeholder text). A row with all empty cells still renders as a row.
  8. A table with only a header and separator (zero body rows) renders as a header-only table, not as raw text.
  9. A single-column table renders as a single-column table (not collapsed to a bullet list or similar).
  10. Malformed tables fall back gracefully:
    • Missing separator row → rendered as preformatted text, not as a table.
    • Ragged rows (some rows have fewer or more cells than the header) → missing cells render empty; extra cells are shown, with the header row extended visually if possible. The block should never silently drop data.
    • Unclosed table (last row truncated mid-stream) → rendered as a partial table; see (11).
  11. Streaming output: while a command is still producing rows, the table renders incrementally. New rows append as they arrive. The header row locks in as soon as the separator line is received; rows before the separator render as plain text until the table is recognized.
  12. Selection and copy:
    • Selecting across cells with the mouse or keyboard selects their visible text content.
    • Copying the selection produces tab-separated plain text by default (one row per line, cells separated by tabs). An affordance (context menu, shortcut) lets the user copy the original markdown source instead.
    • Copying the entire block preserves the original markdown source verbatim.
  13. Search within a block (find-in-block) matches against cell text content. Matches highlight in place in the rendered cell; navigating matches scrolls the table into view, including horizontally if the match is in an off-screen column.
  14. Sharing or exporting a block (Warp Drive, share link, save as file) preserves the original markdown source, not the rendered form.
  15. Theming: table borders, header backgrounds, alternating row shading (if any), and link/code styles all come from the active Warp theme. No hard-coded colors.
  16. Markdown tables render consistently wherever block-list markdown already renders — command output, agent responses, and any other block type that supports inline markdown. The same input produces the same table in each surface.
  17. Non-table pipe content is not misrendered as a table. Text that contains
    |
    characters but no valid header-separator line remains plain text, even if it visually resembles a table.
undefined
  1. 当终端输出块包含GitHub风格的Markdown表格(由表头行、一行或多行
    ---
    分隔符行、以及一行或多行正文行组成,所有行均由
    |
    分隔)时,该表格将在块中以可视化格式渲染,而非原始的竖线分隔文本。
  2. 表格渲染时具备以下特性:
    • 视觉上区分度明显的表头行。
    • 根据分隔符行对齐列:
      |:---|
      左对齐,
      |:---:|
      居中,
      |---:|
      右对齐。无冒号的
      |---|
      将回退到默认对齐方式(文本左对齐,类数值内容右对齐)。
    • 与当前主题一致的可见行分隔符(或等效间距)。
  3. 单元格内的行内Markdown将行内渲染:粗体、斜体、行内代码、删除线及链接的渲染方式与块输出中的其他内容一致。单元格内的换行符(
    <br>
    或转义的
    \n
    )将渲染为单元格内换行。
  4. 列宽选择需适应表格的自然内容(当内容可容纳在块内时)。若单个单元格内容过长,该单元格将在列内换行,而非强制列宽到不合理的程度。
    • 待解决问题: 当换行后的单元格导致行高过高时,是添加“展开”控件进行截断,还是允许行高无限制增长?
  5. 水平滚动:当表格总宽度超过块宽度(多列或无法合理缩小的宽列)时,表格将在块内变为可水平滚动。水平滚动可显示屏幕外的列,无需截断或裁剪内容。块的垂直滚动功能不受表格滚动影响,可独立工作。
  6. 当块大小调整时(终端窗口调整、面板拆分、侧边栏打开/关闭),表格将重新适配新宽度,且行和列的顺序不会丢失。
  7. 空单元格将渲染为可见的空状态(行高与相邻单元格一致,无占位文本)。所有单元格均为空的行仍会渲染为一行。
  8. 仅包含表头和分隔符(零行正文)的表格将渲染为仅表头的表格,而非原始文本。
  9. 单列表格将渲染为单列表格(不会折叠为项目符号列表或类似形式)。
  10. 格式错误的表格将优雅降级:
    • 缺少分隔符行 → 渲染为预格式化文本,而非表格。
    • 不规则行(部分行的单元格数量与表头不同) → 缺失的单元格渲染为空;多余的单元格将显示,表头行将尽可能视觉上扩展以适配。块绝不能静默丢弃数据。
    • 未闭合的表格(最后一行在流式输出中被截断) → 渲染为部分表格;详见第11条。
  11. 流式输出:当命令仍在生成行时,表格将增量渲染。新行到达时将追加显示。表头行在接收到分隔符行后即固定;分隔符行之前的行将渲染为纯文本,直到表格被识别。
  12. 选择与复制:
    • 使用鼠标或键盘跨单元格选择时,将选中单元格的可见文本内容。
    • 默认情况下,复制选中内容将生成制表符分隔的纯文本(每行对应表格的一行,单元格由制表符分隔)。提供操作入口(上下文菜单、快捷键)让用户可选择复制原始Markdown源。
    • 复制整个块将完整保留原始Markdown源。
  13. 块内搜索(块内查找)将匹配单元格的文本内容。匹配结果将在渲染的单元格中高亮显示;导航匹配结果时,表格将滚动到视图中,若匹配结果在屏幕外列,将自动水平滚动。
  14. 分享或导出块(Warp Drive、分享链接、保存为文件)将保留原始Markdown源,而非渲染后的形式。
  15. 主题适配:表格边框、表头背景、交替行着色(若有)及链接/代码样式均来自当前Warp主题。无硬编码颜色。
  16. Markdown表格在所有支持块列表Markdown的界面中渲染一致——命令输出、Agent响应及其他支持行内Markdown的块类型。相同输入在每个界面中生成相同的表格。
  17. 非表格的竖线内容不会被误渲染为表格。包含
    |
    字符但无有效表头分隔符行的文本仍将显示为纯文本,即使其视觉上类似表格。
undefined