contextual-commit

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Contextual Commits

上下文提交(Contextual Commits)

You write commits that carry development reasoning in the body — the intent, decisions, constraints, and learnings that the diff alone cannot show.
你编写的提交信息会在正文携带开发逻辑——diff本身无法体现的意图、决策、约束和经验教训。

The Problem You Solve

解决的问题

Standard commits preserve WHAT changed. The diff shows that too. What gets lost is WHY — what the user asked for, what alternatives were considered, what constraints shaped the implementation, what was learned along the way. This context evaporates when the session ends. You prevent that.
标准提交仅记录了变更内容,diff也能展示同样的信息。但背后的原因会被丢失:用户的需求是什么、考虑过哪些备选方案、实现受哪些约束影响、过程中获得了哪些经验教训。这些上下文会在会话结束后消失,而你可以避免这种情况。

Commit Format

提交格式

The subject line is a standard Conventional Commit. The body contains action lines — typed, scoped entries that capture reasoning.
type(scope): subject line (standard conventional commit)

action-type(scope): description of reasoning or context
action-type(scope): another entry
主题行是标准的Conventional Commit格式。正文包含操作行——带类型、带范围的条目,用于记录逻辑背景。
type(scope): subject line (standard conventional commit)

action-type(scope): description of reasoning or context
action-type(scope): another entry

Subject Line

主题行

Follow Conventional Commits exactly. Nothing changes here:
  • feat(auth): implement Google OAuth provider
  • fix(payments): handle currency rounding edge case
  • refactor(notifications): extract digest scheduling logic
严格遵循Conventional Commits规范,无需调整:
  • feat(auth): implement Google OAuth provider
  • fix(payments): handle currency rounding edge case
  • refactor(notifications): extract digest scheduling logic

Action Lines

操作行

Each line in the body follows:
action-type(scope): description
scope is a human-readable concept label — the domain area, module, or concern. Examples:
auth
,
payment-flow
,
oauth-library
,
session-store
,
api-contracts
. Use whatever is meaningful in this project's vocabulary. Keep scopes consistent across commits when referring to the same concept.
正文的每一行都遵循:
action-type(scope): 描述
scope是人类可读的概念标签——可以是领域范围、模块或关注点。例如:
auth
payment-flow
oauth-library
session-store
api-contracts
。使用项目术语体系中有意义的表述即可,指代同一概念时保持各提交的scope一致。

Action Types

操作类型

Use only the types that apply. Most commits need 1-3 action lines. Never pad with noise.
仅使用适用的类型,大多数提交需要1-3条操作行,不要添加无意义的内容。

intent(scope): ...

intent(scope): ...

What the user wanted to achieve and why. Captures the human's voice, not your interpretation.
  • intent(auth): social login starting with Google, then GitHub and Apple
  • intent(notifications): users want batch notifications instead of per-event emails
  • intent(payment-flow): must support EUR and GBP alongside USD for enterprise clients
When to use: Most feature work, refactoring with a purpose, any change where the motivation isn't obvious from the subject line.
用户想要实现的目标及原因,记录用户的原话而非你的解读。
  • intent(auth): social login starting with Google, then GitHub and Apple
  • intent(notifications): users want batch notifications instead of per-event emails
  • intent(payment-flow): must support EUR and GBP alongside USD for enterprise clients
适用场景: 大多数功能开发、有明确目的的重构、所有动机无法从主题行直观看出的变更。

decision(scope): ...

decision(scope): ...

What approach was chosen when alternatives existed. Brief reasoning.
  • decision(oauth-library): passport.js over auth0-sdk for multi-provider flexibility
  • decision(digest-schedule): weekly on Monday 9am, not daily — matches user research
  • decision(currency-handling): per-transaction currency over account-level default
When to use: When you evaluated options. Skip for obvious choices with no real alternatives.
存在备选方案时选择的实现方式,附带简短的选择理由。
  • decision(oauth-library): passport.js over auth0-sdk for multi-provider flexibility
  • decision(digest-schedule): weekly on Monday 9am, not daily — matches user research
  • decision(currency-handling): per-transaction currency over account-level default
适用场景: 你评估过多个选项时使用,没有备选方案的常规选择不需要添加。

rejected(scope): ...

rejected(scope): ...

What was considered and explicitly discarded, with the reason. This is the highest-value action type — it prevents future sessions from re-proposing the same thing.
  • rejected(oauth-library): auth0-sdk — locks into their session model, incompatible with redis store
  • rejected(currency-handling): account-level default — too limiting for marketplace sellers
  • rejected(money-library): accounting.js — lacks support for sub-unit (cents) arithmetic
When to use: Every time you or the user considered a meaningful alternative and chose not to pursue it. Always include the reason.
考虑过但明确放弃的方案及原因,这是价值最高的操作类型——可以避免后续会话重复提出相同的方案。
  • rejected(oauth-library): auth0-sdk — locks into their session model, incompatible with redis store
  • rejected(currency-handling): account-level default — too limiting for marketplace sellers
  • rejected(money-library): accounting.js — lacks support for sub-unit (cents) arithmetic
适用场景: 每次你或用户考虑过有价值的备选方案并决定不采用时使用,必须附带原因。

constraint(scope): ...

constraint(scope): ...

Hard limits, dependencies, or boundaries discovered during implementation that shaped the approach.
  • constraint(callback-routes): must follow /api/auth/callback/:provider pattern per existing convention
  • constraint(stripe-integration): currency required at PaymentIntent creation, cannot change after
  • constraint(session-store): redis 24h TTL means tokens must refresh within that window
When to use: When non-obvious limitations influenced the implementation. Things the next person working here would need to know.
实现过程中发现的、影响实现方案的硬性限制、依赖或边界。
  • constraint(callback-routes): must follow /api/auth/callback/:provider pattern per existing convention
  • constraint(stripe-integration): currency required at PaymentIntent creation, cannot change after
  • constraint(session-store): redis 24h TTL means tokens must refresh within that window
适用场景: 非显而易见的限制影响了实现方案时使用,是后续开发该模块的开发者需要了解的信息。

learned(scope): ...

learned(scope): ...

Something discovered during implementation that would save time in future sessions. API quirks, undocumented behavior, performance characteristics.
  • learned(passport-google): requires explicit offline_access scope for refresh tokens, undocumented in quickstart
  • learned(stripe-multicurrency): presentment currency and settlement currency are different concepts
  • learned(exchange-rates): Stripe handles conversion — do NOT store our own rates
When to use: "I wish I'd known this before I started" moments. Library gotchas, API surprises, non-obvious behaviors.
实现过程中发现的、可以节省后续开发时间的信息,比如API特性、未归档的行为、性能特征等。
  • learned(passport-google): requires explicit offline_access scope for refresh tokens, undocumented in quickstart
  • learned(stripe-multicurrency): presentment currency and settlement currency are different concepts
  • learned(exchange-rates): Stripe handles conversion — do NOT store our own rates
适用场景: 「要是我开始开发前知道这点就好了」的时刻,比如库的坑、API的意外行为、非显而易见的特性。

Before You Write the Commit

编写提交信息前

Determine the commit scope, then compose action lines:
  1. Check for staged changes first — run
    git diff --cached --stat
    .
    • If staged changes exist: these are the commit scope. Do not consider unstaged or untracked files — the user has already expressed what belongs in this commit by staging it.
    • If nothing is staged: consider all unstaged modifications and untracked files as candidates. Use session context and the diff to decide what to stage and commit.
  2. Identify what you have session context for — changes you produced, discussed, or observed reasoning for during this conversation.
  3. Identify what you don't — files or changes from a prior session, another agent, or manual edits outside this conversation.
  4. Write action lines accordingly:
    • For changes you have context for: full action lines from session knowledge.
    • For changes you don't: apply the "When You Lack Conversation Context" rules below — write only what the diff evidences.
The commit message must account for ALL changes in the commit scope, not just the ones you worked on. Ignoring changes you didn't produce is worse than writing thin action lines for them.
确定提交范围,然后编写操作行:
  1. 首先检查暂存变更 — 执行
    git diff --cached --stat
    • 如果存在暂存变更: 这些就是本次提交的范围,不需要考虑未暂存或未跟踪的文件——用户已经通过暂存操作明确了本次提交包含的内容。
    • 如果没有暂存内容: 将所有未暂存的修改和未跟踪的文件作为候选,结合会话上下文和diff决定需要暂存和提交的内容。
  2. 识别你拥有会话上下文的内容 — 本次会话中你生成、讨论过或者了解背后逻辑的变更。
  3. 识别你没有上下文的内容 — 来自之前会话、其他Agent的变更,或者本次会话之外的手动编辑内容。
  4. 对应编写操作行:
    • 对于你有上下文的变更:基于会话知识编写完整的操作行。
    • 对于你没有上下文的变更:遵循下方「缺少会话上下文时」的规则——仅编写diff可以明确证明的内容。
提交信息必须覆盖提交范围内的所有变更,而不仅仅是你参与开发的部分。忽略你没参与的变更,比对这些变更编写精简的操作行更糟糕。

Examples

示例

Simple fix — no action lines needed

简单修复 — 不需要操作行

fix(button): correct alignment on mobile viewport
The conventional commit subject is sufficient. Don't add noise.
fix(button): correct alignment on mobile viewport
Conventional Commit的主题已经足够,不需要添加冗余内容。

Moderate feature

中等复杂度的功能

feat(notifications): add email digest for weekly summaries

intent(notifications): users want batch notifications instead of per-event emails
decision(digest-schedule): weekly on Monday 9am — matches user research feedback
constraint(email-provider): SendGrid batch API limited to 1000 recipients per call
feat(notifications): add email digest for weekly summaries

intent(notifications): users want batch notifications instead of per-event emails
decision(digest-schedule): weekly on Monday 9am — matches user research feedback
constraint(email-provider): SendGrid batch API limited to 1000 recipients per call

Complex architectural change

复杂的架构变更

refactor(payments): migrate from single to multi-currency support

intent(payments): enterprise customers need EUR and GBP alongside USD
intent(payment-architecture): must be backward compatible, existing USD flows unchanged
decision(currency-handling): per-transaction currency over account-level default
rejected(currency-handling): account-level default too limiting for marketplace sellers
rejected(money-library): accounting.js — lacks sub-unit arithmetic, using currency.js instead
constraint(stripe-integration): Stripe requires currency at PaymentIntent creation, cannot change after
constraint(database-migration): existing amount columns need companion currency columns, not replacement
learned(stripe-multicurrency): presentment currency vs settlement currency are different Stripe concepts
learned(exchange-rates): Stripe handles conversion, we should NOT store our own rates
refactor(payments): migrate from single to multi-currency support

intent(payments): enterprise customers need EUR and GBP alongside USD
intent(payment-architecture): must be backward compatible, existing USD flows unchanged
decision(currency-handling): per-transaction currency over account-level default
rejected(currency-handling): account-level default too limiting for marketplace sellers
rejected(money-library): accounting.js — lacks sub-unit arithmetic, using currency.js instead
constraint(stripe-integration): Stripe requires currency at PaymentIntent creation, cannot change after
constraint(database-migration): existing amount columns need companion currency columns, not replacement
learned(stripe-multicurrency): presentment currency vs settlement currency are different Stripe concepts
learned(exchange-rates): Stripe handles conversion, we should NOT store our own rates

Mid-implementation pivot

开发中途调整方案

When intent changes during work, capture it on the commit where the pivot happens:
refactor(auth): switch from session-based to JWT tokens

intent(auth): original session approach incompatible with redis cluster setup
rejected(auth-sessions): redis cluster doesn't support session stickiness needed by passport sessions
decision(auth-tokens): JWT with short expiry + refresh token pattern
learned(redis-cluster): session affinity requires sticky sessions at load balancer level — too invasive
当开发过程中意图发生变化时,在调整方案对应的提交中记录变更:
refactor(auth): switch from session-based to JWT tokens

intent(auth): original session approach incompatible with redis cluster setup
rejected(auth-sessions): redis cluster doesn't support session stickiness needed by passport sessions
decision(auth-tokens): JWT with short expiry + refresh token pattern
learned(redis-cluster): session affinity requires sticky sessions at load balancer level — too invasive

When You Lack Conversation Context

缺少会话上下文时

Sometimes staged changes include work you didn't produce in this session — prior session output, another agent's changes, pasted code, externally generated files, or manual edits. For any change where you lack the reasoning trail:
Only write action lines for what is clearly evidenced in the diff. Do not speculate about intent or constraints you cannot observe.
What you CAN infer from a diff alone:
  • decision(scope)
    — if a clear technical choice is visible (new dependency added, pattern adopted, library switched). Example:
    decision(http-client): switched from axios to native fetch
    is visible from the diff.
What you CANNOT infer — do not fabricate:
  • intent(scope)
    — why the change was made is not in the diff. Don't restate what the diff shows.
  • rejected(scope)
    — what was NOT chosen is invisible in what WAS committed.
  • constraint(scope)
    — hard limits are almost never visible in code changes.
  • learned(scope)
    — learnings come from the process, not the output.
A clean conventional commit subject with no action lines is always better than fabricated context.
有时暂存变更包含你未在本次会话中生成的内容——之前会话的输出、其他Agent的变更、粘贴的代码、外部生成的文件或者手动编辑的内容。对于所有你不了解背后逻辑的变更:
仅为diff可以明确证明的内容编写操作行,不要臆测你无法观察到的意图或约束。
仅通过diff可以推断的内容:
  • decision(scope)
    — 如果diff中可以看到明确的技术选择(新增依赖、采用新模式、切换库)。例如:diff中可以明显看出
    decision(http-client): switched from axios to native fetch
无法通过diff推断的内容——不要编造:
  • intent(scope)
    — 变更的原因不会出现在diff中,不要重复diff已经展示的内容。
  • rejected(scope)
    — 放弃的方案不会出现在最终提交的代码中。
  • constraint(scope)
    — 硬性限制几乎不会体现在代码变更中。
  • learned(scope)
    — 经验教训来自开发过程,而非最终产出。
干净的Conventional Commit主题+没有操作行,永远比编造的上下文更好。

Git Workflows

Git工作流适配

Contextual commits work with every standard git workflow. No special handling needed.
  • Regular merges: Commit bodies preserved intact.
  • Squash merges: All commit bodies concatenated into the squash commit body. The result is a chronological trail of typed, scoped action lines — agents parse, filter, and group these without issue.
  • Rebase and cherry-pick: Commit bodies preserved.
上下文提交兼容所有标准Git工作流,不需要特殊处理。
  • 普通合并: 提交正文会完整保留。
  • 压缩合并: 所有提交的正文会拼接为压缩提交的正文,最终得到按时间排序的带类型、带范围的操作行记录——Agent可以毫无障碍地解析、筛选和分组这些内容。
  • 变基和拣选: 提交正文会完整保留。

Rules

规则

  1. The subject line is a Conventional Commit. Never break existing conventions or tooling.
  2. Action lines go in the body only. Never in the subject line.
  3. Only write action lines that carry signal. If the diff already explains it, don't repeat it. If there was nothing to decide, reject, or discover, write no action lines.
  4. Be concise but complete. Each action line should be a single clear statement. No artificial length limits, but don't write essays either.
  5. Use consistent scopes within a project. If you called it
    auth
    in one commit, don't call it
    authentication
    in the next.
  6. Capture the user's intent in their words. For
    intent
    lines, reflect what the human asked for, not your implementation summary.
  7. Always explain why for
    rejected
    lines.
    A rejection without a reason is useless — the next agent will just re-propose it.
  8. Don't invent action lines for trivial commits. A typo fix, a dependency bump, a formatting change — the conventional commit subject is enough.
  9. Don't fabricate context you don't have. If you weren't part of the reasoning, don't pretend you were. See "When You Lack Conversation Context" above.
  1. 主题行遵循Conventional Commit规范,不要破坏现有规范或工具链。
  2. 操作行仅放在正文里,不要出现在主题行中。
  3. 仅编写有价值的操作行,如果diff已经能说明问题就不要重复。如果没有需要记录的决策、放弃的方案或发现的问题,就不要编写操作行。
  4. 简洁但完整,每条操作行应该是单条清晰的表述,没有人为长度限制,但也不要写长篇大论。
  5. 项目内使用一致的scope,如果某次提交里用了
    auth
    ,下一次指代同一概念时不要改成
    authentication
  6. 用用户的原话记录意图
    intent
    行要反映用户的需求,而非你的实现总结。
  7. rejected
    行必须说明原因
    ,没有原因的放弃方案毫无意义——后续的Agent还会再次提出同样的方案。
  8. ** trivial提交不要编造操作行**,比如错别字修复、依赖升级、格式调整——Conventional Commit的主题已经足够。
  9. 不要编造你没有的上下文,如果你没有参与决策过程,不要假装你了解,参考上文「缺少会话上下文时」的规则。