generate-diffmap
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseLiving spec
动态规范
A spec is one Markdown file at that shows the current system, the plan, and the work done. Update it as the plan changes and work lands. For a walkthrough of an existing pull request, use its spec if it has one; otherwise write under . Check the repo's paths and symbols before writing.
specs/<short-kebab-case-name>.md.diffmap/Settle scope first. Read the relevant code before writing. When the design, scope, or phase split is unclear, check with the user before writing much: offer the options and your pick.
一个规范是位于 的单个 Markdown 文件,用于展示当前系统、计划和已完成的工作。随计划和工作的落地持续更新它。若要为现有 pull request 编写讲解,请使用它已有的规范;如果没有,则写在 下。编写前先检查仓库的路径和符号。
specs/<short-kebab-case-name>.md.diffmap/先确定范围。 编写前先阅读相关代码。如果设计、范围或阶段划分不明确,在大量编写前先与用户确认:列出可选方案以及你的建议。
Serve and share
启动服务与共享
Run from the repo root:
sh
npx @tanishqkancharla/diffmap list # reuse the URL if this file is already served
npx @tanishqkancharla/diffmap serve specs/<name>.md
npx @tanishqkancharla/diffmap share specs/<name>.md # only when asked; prints https://diffmap.dev/g/<id>Leave the server running and give the user the file path and URL. Open the browser only when asked. Before sharing, commit finished work and point its references at the commit, as in When a phase is complete: saves each referenced commit's diff into the gist, but a reference without a commit only opens the file as it was when shared. A shared gist is unlisted: anyone with the URL can read it, including every file it snapshots. A spec on a pull request is also at , pinned to a commit.
sharehttps://diffmap.dev/<owner>/<repo>/pull/<n>/specs/<name>.md从仓库根目录运行:
sh
npx @tanishqkancharla/diffmap list # reuse the URL if this file is already served
npx @tanishqkancharla/diffmap serve specs/<name>.md
npx @tanishqkancharla/diffmap share specs/<name>.md # only when asked; prints https://diffmap.dev/g/<id>让服务器保持运行,并把文件路径和 URL 提供给用户。仅在用户要求时打开浏览器。分享前,提交已完成的工作,并让引用指向该 commit,如 阶段完成时 所述: 会将每个被引用 commit 的 diff 保存到 gist 中,但不带 commit 的引用只会打开分享时的文件版本。共享 gist 不会列入公开列表:任何拿到 URL 的人都能读取,包括其中每个文件快照。pull request 上的规范也可通过 访问,并固定到某个 commit。
sharehttps://diffmap.dev/<owner>/<repo>/pull/<n>/specs/<name>.mdShape
结构
Write for someone who needs to understand the change quickly. Start from what changes for the person using it, such as a screen, command, API call, or error, then work back toward the implementation as far as the reader needs. A spec usually has a problem overview, a solution overview, goals and non-goals, phases, and References; let the content decide what each needs.
Phases. Keep each small enough to commit alone. Headings are plain when planned, ⏳ in progress, and ✅ done. Open each with a short Today and Proposed from the client's view, then explain it however suits the change: a new flow might want a diagram and a call stack, a refactor a call stack diff, a dependency bump a sentence or two. Match the length to the change, and say each thing once, where it belongs. A spec explains the change; it is not a task tracker. End each phase with its fence, after the walkthrough and before the next phase or section, not right after Today and Proposed; leave it empty until the phase is committed.
changesVisuals. Diagrams, call stacks, and code often explain a change better than paragraphs, so reach for one when prose gets long. Add one only when it shows the reader something they need, not to fill a slot.
The views fit one model: services receive messages, update their state, and send messages on. Each view looks between services or inside one, at every path or at one message's trace:
| Every path | One message | |
|---|---|---|
| Between services | | |
| Inside one service | Pseudocode and types: its state, payloads, logic | |
Use when the parts keep state, run at the same time, or fan out; use a call stack when they are function calls that return, branch, or return early. In a diagram, state is what you would find inside a service if you paused between steps; a result passed back is a reply event, not state. To explain a step, put a note under its event, its update, or a service name on its own line. Mermaid sequence and state diagrams are lighter options. If a view needs more than about 7 services, collapse some and explain them separately.
servicesservices>- To show a change, add after
difformermaidand start each line withservices(Today only),-(Proposed only), or a space (both), as in a call stack. For code, use a+fence.diff - Title Mermaid diagrams with after the language, as in
[Title]. Amermaid diff [Invalid records stop before storage]fence usesservicesbefore its first step as the title of the whole diagram, with astart "caption"line under it that summarizes the full process in a few sentences; caption each step with what happens.> - Label nodes and arrows with real names, and link existing source with directives.
%% ref
Call stacks. Write rows as data flow, , with rows where they matter. Mark rows (original), (change), or a space (unchanged); a diff compares the same function before and after. Nest calls with and . Link real functions with source references; an unlinked helper is pseudocode, not a promised API. Keep stacks to about a dozen rows, splitting deep flows into an overview and a stack per changed callee.
result = function(inputs)return-+├──└──Code. Keep fences short and on the point the paragraph makes; elide unchanged code with . Highlight lines with after the language (or rows with after ), and explain with notes at the bottom instead of long comments: for a name wherever it appears, for lines counted from the top of the fence. Call stacks take the same notes with .
// ...{2-3}{3-4}callstack// [!note symbol] text// [!note L3-L5] text#Links. Link existing source with references; write planned symbols as plain text.
面向需要快速理解变更的读者来写。先讲使用方式发生了什么变化,例如界面、命令、API 调用或错误,再按读者需要回溯到实现。规范通常包括问题概述、解决方案概述、目标与非目标、阶段以及 References;具体内容决定各部分需要什么。
阶段。 每个阶段都应小到可以单独提交。计划中的标题使用普通文本,进行中标记 ⏳,已完成标记 ✅。每个阶段先从客户端视角简要写出 Today 和 Proposed,再以适合变更的方式解释:新流程可能需要图和 call stack,重构可能需要 call stack diff,依赖升级可能只需一两句话。篇幅应与变更规模相称;每件事只说一次,并放在正确的位置。规范用于解释变更,不是任务跟踪器。在讲解之后、下一个阶段或章节之前,以 fence 结束每个阶段;不要紧接在 Today 和 Proposed 之后。阶段提交前让它保持为空。
changes可视化内容。 当段落太长时,图表、call stack 和代码通常比长篇说明更有效。仅在能向读者展示其所需信息时添加,而不是为了填满位置。
这些视图都遵循同一个模型:服务接收消息、更新自身状态,然后继续发送消息。每个视图既可以观察服务之间,也可以观察单个服务内部;既可以覆盖所有路径,也可以跟踪一条消息:
| 每条路径 | 单条消息 | |
|---|---|---|
| 服务之间 | | |
| 单个服务内部 | 伪代码和类型:状态、payload、逻辑 | |
当各部分保存状态、同时运行或产生扇出时,使用 ;当它们是返回、分支或提前返回的函数调用时,使用 call stack。在 图中,状态是指如果在步骤之间暂停时能在服务内部找到的内容;传回的结果是回复事件,而不是状态。要解释某个步骤,可在事件、更新操作,或单独一行的服务名称下添加 注释。Mermaid 序列图和状态图是更轻量的选择。如果一个视图包含超过约 7 个服务,折叠其中一些并另行说明。
servicesservices>- 要展示变更,在 或
mermaid后添加services,并让每行以diff(仅 Today)、-(仅 Proposed)或空格(两者都有)开头,与 call stack 相同。代码使用+fence。diff - 用语言标识后的 为 Mermaid 图命名,例如
[Title]。mermaid diff [Invalid records stop before storage]fence 在第一步之前使用services作为整张图的标题,并在其下用start "caption"行以几句话概括完整流程;每一步都标注发生了什么。> - 用真实名称标注节点和箭头,并通过 指令关联现有源代码。
%% ref
Call stacks。 将各行写成数据流形式 ,必要时加入 行。将行标记为 (原始)、(变更)或空格(未变);diff 比较同一函数变更前后的版本。使用 和 表示嵌套调用。使用源代码引用关联真实函数;未链接的 helper 是伪代码,不代表对外承诺的 API。调用栈应控制在约十几行;将深层流程拆成概览和每个变更被调用函数的独立调用栈。
result = function(inputs)return-+├──└──代码。 让 fence 保持简短,并紧扣段落要表达的重点;用 省略未变更的代码。在语言后使用 高亮行(或者在 后使用 高亮行),并在底部用注释说明,而不是写冗长注释:对于无论在何处出现的名称,使用 ;对于从 fence 顶部开始计数的行,使用 。Call stack 使用相同的注释格式,但以 开头。
// ...{2-3}callstack{3-4}// [!note symbol] text// [!note L3-L5] text#链接。 使用 引用 关联现有源代码;计划中的符号使用纯文本。
Prose
文字表述
Follow Orwell's six rules from "Politics and the English Language" in all prose, comments, and diagram labels. Code names are not jargon.
所有正文、注释和图表标签都遵循《政治与英语语言》中的奥威尔六条规则。代码名称不属于行话。
Example phase
示例阶段
This example uses illustrative files and a commit SHA, not Diffmap source paths. Its change crosses a few layers, so it uses several views; a smaller phase needs fewer.
md
undefined此示例使用示意文件和 commit SHA,并非 diffmap 的实际源代码路径。该变更跨越多个层,因此使用了多个视图;较小的阶段不需要这么多视图。
md
undefined⏳ Phase 1: Validate request input
⏳ Phase 1: Validate request input
Today
The handler passes records straight to the service. A client that sends an empty name gets a success response, and the bad record surfaces later as a storage error the client never sees, so they keep sending bad input.
Proposed
A client that sends an empty name gets an error naming the field, so they can fix the request right away. Bulk import in Phase 2 needs every saved record to be valid, so this check comes first.
nameFrom the client's side, the same empty-name request now fails at once:
ts
const response = await client.saveRecord({ name: "" });
// before: { ok: true }
// after: { ok: false, error: "name must not be empty" }Today every record reaches storage. In the proposed flow, a valid record follows the same save path, but an invalid one returns an error before storage. The unlinked helper is pseudocode, not a proposed public API:
validateInputmermaid
flowchart LR
- A[requestHandler] -->|record| B[existingService]
+ A[requestHandler] -->|record| V[validateInput]
+ V -->|valid record| B[existingService]
+ V -->|invalid record| E[Return name error]
B -->|record| D[Storage]
%% ref node:A [[src/request.ts#requestHandler]]
%% ref node:B [[src/service.ts#existingService]]The call stack shows how the validation result controls the return path:
callstack
response = requestHandler(record) [[src/request.ts#requestHandler]]
-└── return existingService(record) [[src/service.ts#existingService]]
+├── error = validateInput(record) # reject empty names
+├── if error: return error
+├── result = existingService(record) [[src/service.ts#existingService]]
+│ ├── storage.save(record)
+│ └── return { ok: true }
+└── return resultIn code, the handler returns before the existing save:
ts
function requestHandler(record: RecordInput) {
const error = validateInput(record);
if (error) return error;
return existingService(record);
}
// [!note validateInput] Returns an error when `name` is blank, otherwise undefined.
// [!note L3] Storage never sees an invalid record.The new check:
ts
function validateInput(record: RecordInput) {
if (record.name.trim() === "")
return { ok: false, error: "name must not be empty" };
return undefined;
}changes
undefinedundefinedToday
The handler passes records straight to the service. A client that sends an empty name gets a success response, and the bad record surfaces later as a storage error the client never sees, so they keep sending bad input.
Proposed
A client that sends an empty name gets an error naming the field, so they can fix the request right away. Bulk import in Phase 2 needs every saved record to be valid, so this check comes first.
nameFrom the client's side, the same empty-name request now fails at once:
ts
const response = await client.saveRecord({ name: "" });
// before: { ok: true }
// after: { ok: false, error: "name must not be empty" }Today every record reaches storage. In the proposed flow, a valid record follows the same save path, but an invalid one returns an error before storage. The unlinked helper is pseudocode, not a proposed public API:
validateInputmermaid
flowchart LR
- A[requestHandler] -->|record| B[existingService]
+ A[requestHandler] -->|record| V[validateInput]
+ V -->|valid record| B[existingService]
+ V -->|invalid record| E[Return name error]
B -->|record| D[Storage]
%% ref node:A [[src/request.ts#requestHandler]]
%% ref node:B [[src/service.ts#existingService]]The call stack shows how the validation result controls the return path:
callstack
response = requestHandler(record) [[src/request.ts#requestHandler]]
-└── return existingService(record) [[src/service.ts#existingService]]
+├── error = validateInput(record) # reject empty names
+├── if error: return error
+├── result = existingService(record) [[src/service.ts#existingService]]
+│ ├── storage.save(record)
+│ └── return { ok: true }
+└── return resultIn code, the handler returns before the existing save:
ts
function requestHandler(record: RecordInput) {
const error = validateInput(record);
if (error) return error;
return existingService(record);
}
// [!note validateInput] Returns an error when `name` is blank, otherwise undefined.
// [!note L3] Storage never sees an invalid record.The new check:
ts
function validateInput(record: RecordInput) {
if (record.name.trim() === "")
return { ok: false, error: "name must not be empty" };
return undefined;
}changes
undefinedundefinedWhen a phase is complete
阶段完成时
Before starting the next phase:
- Commit the code. If the phase took several commits, use wherever these steps say SHA.
<base>..<head> - Put that SHA in the phase's fence at the end of the phase. It renders the phase's summary card. Under the SHA, list the files a reviewer should read, one path per line, in the order to read them: usually the client-facing entry point first, then down the layers. Leave out files that need no thought, such as lockfiles, generated code, snapshots, fixtures, and mechanical renames; the card folds them under "N other files".
changes - Point every reference the phase changed at the commit: stack rows, directives, and prose.
%% ref - Update diagrams, call stacks, and pseudocode to match what was built, and mark the heading ✅.
- Commit the spec update separately; it names the code commit's SHA.
- Restart ; it checks commit references at startup.
serve
The example phase keeps its walkthrough, with references pointing at the commit, and ends with:
md
```callstack
response = requestHandler(record) [[a1b2c3d:src/request.ts#L8-L20]]
-└── return existingService(record) [[src/service.ts#existingService]]
+├── error = validateInput(record) [[a1b2c3d:src/validate.ts#L1-L9]]
+├── if error: return error
+├── result = existingService(record) [[src/service.ts#existingService]]
+│ ├── storage.save(record)
+│ └── return { ok: true }
+└── return result
```
```changes
a1b2c3d
src/request.ts
src/validate.ts
```开始下一个阶段之前:
- 提交代码。如果该阶段包含多个 commit,则在这些步骤提到 SHA 的位置使用 。
<base>..<head> - 将该 SHA 放在阶段末尾的 fence 中。它会渲染阶段的摘要卡片。在 SHA 下方,每行列出一个审阅者应阅读的文件,并按阅读顺序排列:通常先列面向客户端的入口点,再逐层向下。无需思考的文件可以省略,例如 lockfile、生成代码、快照、fixtures 和机械性重命名;卡片会将它们折叠到“N other files”之下。
changes - 让该阶段变更涉及的每处引用都指向这个 commit:call stack 行、指令和正文引用。
%% ref - 更新图、call stack 和伪代码,使其与实际实现一致,并将标题标记为 ✅。
- 单独提交规范更新;其中写明代码 commit 的 SHA。
- 重启 ;它会在启动时检查 commit 引用。
serve
示例阶段保留了讲解,引用指向该 commit,并以以下内容结束:
md
```callstack
response = requestHandler(record) [[a1b2c3d:src/request.ts#L8-L20]]
-└── return existingService(record) [[src/service.ts#existingService]]
+├── error = validateInput(record) [[a1b2c3d:src/validate.ts#L1-L9]]
+├── if error: return error
+├── result = existingService(record) [[src/service.ts#existingService]]
+│ ├── storage.save(record)
+│ └── return { ok: true }
+└── return result
```
```changes
a1b2c3d
src/request.ts
src/validate.ts
```Source references
源代码引用
Paths are relative to the repo root.
- links a TypeScript or JavaScript declaration; qualify ambiguous ones, as in
[[path/to/file.ts#symbolName]].[[src/store.ts#Store.save]] - links a file;
[[path]]a line range.[[path#L12-L30]] - links lines in a commit, or
[[<sha>:path#L12-L30]]for a phase over several commits, where<base>..<head>is the commit before the first. Lines are numbered after the commit, so deleted lines cannot be linked. Find them with<base>andgit show <sha> -- <path>.git show <sha>:<path> | nl -ba - A reference without a commit opens that file's uncommitted changes, or the current file when it has none. A shared gist opens the file as it was when shared.
- In prose, references render as clickable inline code; wrap one in backticks to show the syntax.
- In Mermaid, links a node or participant and
%% ref node:<id> [[reference]]an edge or message, counting from zero in declaration order. Use one directive per target, with several references when needed.%% ref edge:<index> [[reference]]
Fences: draws a diagram, an animated diagram of services, their state, and the events between them (add after either to mark lines or ), stack rows, a commit or range summary (empty for uncommitted changes) with the files to review listed under it, and trusted HTML you wrote. Other languages are pseudocode and types, with optional highlights and lines. after the language, as in , titles a call stack, code fence, or Mermaid diagram.
mermaidservicesdiff-+callstackchangeshtml{…}[!note …][Title]```callstack [Request flow]路径相对于仓库根目录。
- 链接 TypeScript 或 JavaScript 声明;如果存在歧义,应限定名称,例如
[[path/to/file.ts#symbolName]]。[[src/store.ts#Store.save]] - 链接文件;
[[path]]链接行范围。[[path#L12-L30]] - 链接某个 commit 中的行;对于跨越多个 commit 的阶段,使用
[[<sha>:path#L12-L30]],其中<base>..<head>是第一个 commit 之前的 commit。行号按照 commit 后的内容计算,因此无法链接已删除的行。可使用<base>和git show <sha> -- <path>查找。git show <sha>:<path> | nl -ba - 不带 commit 的引用会打开文件的未提交更改;如果没有未提交更改,则打开当前文件。共享 gist 会打开分享时的文件版本。
- 在正文中,引用会呈现为可点击的行内代码;用反引号包裹可显示其语法。
- 在 Mermaid 中,链接节点或参与者,
%% ref node:<id> [[reference]]链接边或消息,索引从零开始,按声明顺序计算。每个目标使用一条指令,需要时可包含多个引用。%% ref edge:<index> [[reference]]
Fence 语法: 绘制图表, 绘制服务、其状态及服务间事件的动画图(在任一者后添加 ,以标记 或 行), 表示 call stack 行, 表示 commit 或范围摘要(未提交的变更则留空),并在其下列出待审阅文件, 表示你编写的受信任 HTML。其他语言表示伪代码和类型,可选 高亮和 行。在语言后使用 (如 )可为 call stack、代码 fence 或 Mermaid 图添加标题。
mermaidservicesdiff-+callstackchangeshtml{…}[!note …][Title]```callstack [Request flow]Feedback
反馈
When diffmap gets in your way or the user has feedback on it, such as a rendering bug, a confusing error, a missing feature, or advice in this skill that did not work, open an issue on the diffmap repo. Search first, and comment on a matching issue instead of opening a duplicate:
sh
gh issue list --repo tanishqkancharla/diffmap --search "<keywords>" --state all
gh issue create --repo tanishqkancharla/diffmap --title "<short summary>" --body "<details>"Say what you did, what you expected, and what happened, with the diffmap version () and the smallest Markdown that shows the problem. Issues are public, so leave out private code, file contents, paths, and gist links unless the user agrees. Give the user the issue URL.
npx @tanishqkancharla/diffmap --version当 diffmap 妨碍工作,或用户对它有反馈时,例如渲染 bug、令人困惑的错误、缺失功能,或本 skill 中的建议无法奏效,请在 diffmap 仓库提交 issue。先搜索;如果已有匹配的 issue,请发表评论,不要创建重复 issue:
sh
gh issue list --repo tanishqkancharla/diffmap --search "<keywords>" --state all
gh issue create --repo tanishqkancharla/diffmap --title "<short summary>" --body "<details>"说明你做了什么、预期发生什么、实际发生了什么,并附上 diffmap 版本()和能展示问题的最小 Markdown。Issue 是公开的,因此除非用户同意,不要包含私有代码、文件内容、路径或 gist 链接。最后向用户提供 issue URL。
npx @tanishqkancharla/diffmap --versionLook up the rest
查看其余内容
- Full reference and fence syntax → diffmap README
- Worked examples → annotations, diagrams, and services fixtures
- 完整参考和 fence 语法 → diffmap README
- 实践示例 → annotations、diagrams 和 services fixtures