bugfix

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Rock RMS Bugfix Workflow

Rock RMS Bug修复工作流

You are fixing a known bug in the Rock RMS codebase. Your job is to understand the root cause, apply the minimal correct fix, and produce a commit message that meets Rock's release-note conventions.
The bug report: $ARGUMENTS

你正在修复Rock RMS代码库中的已知bug。你的任务是理解根本原因,应用最小化的正确修复,并生成符合Rock发布说明规范的提交信息。
bug报告: $ARGUMENTS

Scale to the Bug

适配bug复杂度的处理策略

Not every bug needs the full investigation process. Match effort to complexity:
  • Obvious fix (user gives exact file, line, and what's wrong — e.g., "line 42 uses
    >
    but should be
    >=
    "): Skip straight to Phase 2. Fix it, verify, output the commit message. Don't load reference files or write a root cause summary for something self-evident.
  • Located but unclear (user knows the area but not the exact cause — e.g., "the save button on GroupDetail doesn't persist the description"): Run Phase 1 but keep it proportional. You may not need every sub-step.
  • Vague or complex (user describes a symptom with no location — e.g., "after merging two people, their giving history disappears"): Run the full workflow. Load the reference file. These are the bugs that benefit most from systematic investigation.
Use your judgment. The phases below are a toolkit, not a mandatory checklist.

并非每个bug都需要完整的调查流程。根据复杂度匹配投入的精力:
  • 明显修复(用户提供确切的文件、行号以及问题所在——例如:“第42行使用了
    >
    ,但应该是
    >=
    ”):直接跳到Phase 2。修复问题、验证并输出提交信息。对于不言自明的问题,无需加载参考文件或撰写根本原因摘要。
  • 已定位但原因不明(用户知道问题区域但不清楚确切原因——例如:“GroupDetail上的保存按钮无法持久化描述信息”):执行Phase 1,但保持流程精简。你可能不需要每个子步骤。
  • 模糊或复杂问题(用户仅描述症状但未提供位置——例如:“合并两个人后,他们的捐赠记录消失了”):执行完整工作流。加载参考文件。这类bug最能从系统化调查中获益。
请自行判断。以下阶段是工具包,而非强制检查清单。

Reference Routing Table

参考文件路由表

Reference FileLoad When
references/rock-bug-patterns.md
When the root cause isn't immediately obvious — especially for stale data, NullRef, cache, or cross-layer bugs
Do NOT read reference files for obvious fixes. Load only when investigation is needed.

参考文件加载时机
references/rock-bug-patterns.md
当根本原因不明显时——尤其是针对 stale data、NullRef、缓存或跨层bug
明显修复无需读取参考文件。 仅在需要调查时加载。

Phase 1 — Triage

Phase 1 — 问题分诊

The goal is to understand the bug well enough to fix it correctly on the first attempt. Resist the urge to jump straight to a patch — most regressions come from fixing symptoms instead of causes.
目标是充分理解bug,确保首次修复即正确。不要急于直接打补丁——大多数回归问题源于修复症状而非原因。

1.1 — Parse the Report

1.1 — 解析报告

Extract from
$ARGUMENTS
:
  • Symptom: What's broken? (wrong output, crash, UI glitch, data issue)
  • Location: File paths, block names, entity names, or areas mentioned
  • Issue number: GitHub issue
    #XXXX
    if provided (for the commit message)
  • Reproduction context: Any steps, conditions, or data states that trigger the bug
$ARGUMENTS
中提取:
  • 症状:哪里出问题了?(错误输出、崩溃、UI故障、数据问题)
  • 位置:提到的文件路径、块名称、实体名称或区域
  • 问题编号:如果提供了GitHub issue
    #XXXX
    (用于提交信息)
  • 复现上下文:触发bug的任何步骤、条件或数据状态

1.2 — Locate the Code

1.2 — 定位代码

Start by finding the relevant files. The bug report may give you exact paths, or you may need to search:
  • Block name given → search
    Rock.Blocks/
    for the C# class and
    Rock.JavaScript.Obsidian.Blocks/src/
    for the
    .obs
    component. Check both — the bug could be server-side, client-side, or a mismatch between them.
  • Entity/model name given → search
    Rock/Model/
    for the entity, then find which blocks or services consume it.
  • Error message or stack trace given → grep for the exact error string or the deepest Rock method in the stack trace. That's your starting point, not the top of the stack.
  • Vague description only → search for keywords from the symptom across the codebase. Cast a wide net first (grep for the feature name), then narrow down by reading the most relevant hits.
首先找到相关文件。bug报告可能提供确切路径,或者你需要搜索:
  • 给出块名称 → 在
    Rock.Blocks/
    中搜索C#类,在
    Rock.JavaScript.Obsidian.Blocks/src/
    中搜索
    .obs
    组件。两者都要检查——bug可能出现在服务端、客户端或两者不匹配的地方。
  • 给出实体/模型名称 → 在
    Rock/Model/
    中搜索实体,然后找到使用它的块或服务。
  • 给出错误消息或堆栈跟踪 → 搜索确切的错误字符串或堆栈跟踪中最底层的Rock方法。这是你的起点,而非堆栈顶部。
  • 仅提供模糊描述 → 在代码库中搜索症状相关的关键词。先广泛搜索(grep功能名称),然后通过阅读最相关的结果缩小范围。

1.3 — Investigate the Root Cause

1.3 — 调查根本原因

This is the most important step. Don't jump to conclusions — walk the code systematically.
Before starting, read
references/rock-bug-patterns.md
.
It catalogs Rock-specific bug patterns organized by symptom (stale data, NullRef, security bypass, UI not updating, wrong query results, thread-safety, data loss, cross-layer mismatches). Match the reported symptom to these patterns — they'll shortcut your investigation significantly.
Step A: Find the entry point. Identify the method, event handler, block action, or API endpoint where the buggy behavior starts. For Obsidian blocks, this is often a
BlockAction
method on the C# side or a watcher/event handler on the Vue side. For WebForms, look at event handlers like
btnSave_Click
or page lifecycle methods.
Step B: Walk the execution path. Follow the logic from the entry point through every service call, query, and conditional branch. Read each method you encounter — don't assume what it does based on its name. Pay special attention to:
  • Conditional branches that might take an unexpected path given the bug's trigger conditions
  • Values that could be null, empty, zero, or an unexpected type
  • Queries that might return no results, multiple results, or stale data
  • Properties being set but never persisted (missing
    SaveChanges
    )
  • Order-of-operations issues where something is read before it's written
Step C: Check recent history. If the bug feels like a regression (it "used to work"), run
git log --oneline -20
on the affected file(s) to see recent changes. Read the diffs of suspicious commits — often the bug is a side effect of a recent change that looked correct in isolation.
Step D: Search for related code. Before concluding you've found the bug, grep for other callers of the affected method or other places that set the same property. The bug might be in a caller, not the method itself. Also check if there's a cache layer (Rock caches aggressively) that could be serving stale data.
Step E: Confirm root cause vs. symptom. Ask yourself: "If I fix this specific line, will the bug be fully resolved, or will it just move the failure to a different place?" A symptom fix looks like adding a null check to suppress an exception. A root cause fix looks like ensuring the value is populated correctly upstream so the null never occurs. Prefer the latter.
Step F: Trace across layers (if the bug spans C# ↔ TypeScript ↔ SQL). Many Rock bugs look correct in each layer individually but break at the boundary. If the bug involves an Obsidian block:
  1. Read the C# block class — check what the
    BlockAction
    methods return and what the bags contain.
  2. Read the
    .obs
    Vue component — check what it sends in
    invokeBlockAction
    and what it does with the response.
  3. Verify property names match exactly (C# PascalCase → auto-generated camelCase in TypeScript).
  4. Verify types survive the JSON round-trip (C#
    int?
    → JSON
    null
    vs
    0
    ;
    decimal
    precision;
    DateTime
    string format).
  5. If a query is involved, check that the LINQ translates to the SQL you expect — sometimes EF generates surprising joins or filters.
这是最重要的步骤。不要急于下结论——系统地梳理代码。
开始前,请阅读
references/rock-bug-patterns.md
它按症状(stale data、NullRef、安全绕过、UI不更新、错误查询结果、线程安全、数据丢失、跨层不匹配)分类记录了Rock特有的bug模式。将报告的症状与这些模式匹配——它们能大幅缩短你的调查时间。
步骤A:找到入口点。 确定bug行为开始的方法、事件处理程序、块操作或API端点。对于Obsidian块,通常是C#端的
BlockAction
方法或Vue端的监听器/事件处理程序。对于WebForms,查看
btnSave_Click
等事件处理程序或页面生命周期方法。
步骤B:梳理执行路径。 从入口点开始,跟踪每个服务调用、查询和条件分支的逻辑。阅读遇到的每个方法——不要仅凭名称假设其功能。特别注意:
  • 在bug触发条件下可能走意外分支的条件判断
  • 可能为null、空、零或意外类型的值
  • 可能无结果、多结果或返回stale data的查询
  • 被设置但从未持久化的属性(缺少
    SaveChanges
  • 读取操作早于写入操作的顺序问题
步骤C:检查近期历史。 如果bug看起来是回归问题(“之前正常工作”),对受影响的文件执行
git log --oneline -20
查看近期变更。阅读可疑提交的diff——bug通常是近期变更的副作用,这些变更单独看似乎正确。
步骤D:搜索相关代码。 在确定找到bug之前,grep受影响方法的其他调用者或设置同一属性的其他位置。bug可能出现在调用者中,而非方法本身。还要检查是否存在缓存层(Rock缓存策略很激进)可能返回stale data。
步骤E:确认根本原因vs症状。 问自己:“如果我修复这一行,bug会完全解决吗,还是只是将失败转移到其他地方?” 症状修复看起来像是添加null检查来抑制异常。根本原因修复看起来像是确保上游正确填充值,从而避免null出现。优先选择后者。
步骤F:跨层跟踪(如果bug涉及C# ↔ TypeScript ↔ SQL)。 许多Rock bug在各层单独看都正确,但在边界处出错。如果bug涉及Obsidian块:
  1. 阅读C#块类——检查
    BlockAction
    方法返回的内容以及bags包含的信息。
  2. 阅读
    .obs
    Vue组件——检查
    invokeBlockAction
    发送的内容以及它如何处理响应。
  3. 验证属性名称完全匹配(C# PascalCase → TypeScript自动生成的camelCase)。
  4. 验证类型在JSON往返过程中保持一致(C#
    int?
    → JSON
    null
    vs
    0
    decimal
    精度;
    DateTime
    字符串格式)。
  5. 如果涉及查询,检查LINQ是否转换为预期的SQL——EF有时会生成意外的连接或过滤条件。

1.4 — Identify the Domain

1.4 — 确定领域

Determine the Rock RMS domain for the commit message based on where the bug lives. Use the file path as the primary signal:
Path containsDomain
/AI/
AI
/Api/
or API controllers
API
/Cms/
or
/Blocks/Cms/
CMS
/CheckIn/
Check-in
/Communication/
Communication
/Connection/
Connection
/Core/
Core
/Crm/
CRM
/Engagement/
Engagement
/Event/
Event
/WebFarm/
Farm
/Finance/
Finance
/Group/
Group
/Lava/
Lava
/Lms/
LMS
/Mobile/
Mobile
/Prayer/
Prayer
/Reporting/
Reporting
/Workflow/
Workflow
Unclear or cross-cuttingOther
Valid domains (exactly one):
AI
,
API
,
CMS
,
Check-in
,
Communication
,
Connection
,
Core
,
CRM
,
Engagement
,
Event
,
Farm
,
Finance
,
Group
,
Lava
,
LMS
,
Mobile
,
Prayer
,
Reporting
,
Workflow
,
Other
根据bug所在位置确定Rock RMS领域,用于提交信息。以文件路径为主要依据:
路径包含领域
/AI/
AI
/Api/
或API控制器
API
/Cms/
/Blocks/Cms/
CMS
/CheckIn/
Check-in
/Communication/
Communication
/Connection/
Connection
/Core/
Core
/Crm/
CRM
/Engagement/
Engagement
/Event/
Event
/WebFarm/
Farm
/Finance/
Finance
/Group/
Group
/Lava/
Lava
/Lms/
LMS
/Mobile/
Mobile
/Prayer/
Prayer
/Reporting/
Reporting
/Workflow/
Workflow
不明确或跨领域Other
有效领域(仅选一个):
AI
,
API
,
CMS
,
Check-in
,
Communication
,
Connection
,
Core
,
CRM
,
Engagement
,
Event
,
Farm
,
Finance
,
Group
,
Lava
,
LMS
,
Mobile
,
Prayer
,
Reporting
,
Workflow
,
Other

1.5 — Summarize Before Fixing

1.5 — 修复前总结

Before writing any code, state:
  1. Root cause — one sentence explaining why the bug happens
  2. Fix approach — what you'll change and why that addresses the root cause (not just the symptom)
  3. Blast radius — what else could be affected by this change (callers, cache, UI, API consumers)
If the root cause is ambiguous or you find multiple possible causes, say so and explain your reasoning before proceeding.

在编写任何代码之前,说明:
  1. 根本原因——一句话解释bug发生的原因
  2. 修复方案——你将修改什么以及为什么这能解决根本原因(而非仅症状)
  3. 影响范围——此变更可能影响的其他内容(调用者、缓存、UI、API消费者)
如果根本原因不明确或你发现多个可能的原因,请说明并解释你的推理,然后再继续。

Phase 2 — Fix

Phase 2 — 修复

2.1 — Apply the Minimal Correct Fix

2.1 — 应用最小化正确修复

Change only what's necessary to resolve the root cause. Follow Rock conventions:
  • Always use braces, even for single-line
    if
    /
    for
    /
    else
    .
  • Use early returns to keep code flat.
  • Use
    RockDateTime
    instead of
    DateTime
    .
  • Don't add optional parameters to public methods — add an overload if needed.
  • Wrap any
    System.Web
    usage in
    #if WEBFORMS
    .
  • If the fix involves a query change, verify the LINQ pattern follows Rock conventions (avoid
    Guid
    in
    .Where()
    when
    Id
    is available, use service-layer predicates over inline duplication).
仅修改解决根本原因所需的内容。遵循Rock规范:
  • 始终使用大括号,即使是单行
    if
    /
    for
    /
    else
  • 使用提前返回保持代码扁平化。
  • 使用
    RockDateTime
    而非
    DateTime
  • 不要向公共方法添加可选参数——如果需要,添加重载。
  • 将任何
    System.Web
    用法包裹在
    #if WEBFORMS
    中。
  • 如果修复涉及查询变更,验证LINQ模式遵循Rock规范(当
    Id
    可用时避免在
    .Where()
    中使用
    Guid
    ,使用服务层谓词而非内联重复代码)。

2.2 — Add an Engineering Note (If Non-Obvious)

2.2 — 添加工程注释(若非显而易见)

If the fix would confuse a future reader — because the root cause is subtle, the fix looks wrong at first glance, or there's a non-obvious constraint — add an engineering note:
csharp
/*
    [Date] - [Author]

    [Why this fix exists and what it prevents.]

    Reason: [One-line summary.]
*/
Skip the note if the fix is self-explanatory (e.g., changing
>
to
>=
).
如果修复会让未来的读者困惑——因为根本原因很微妙、修复乍看有误,或者存在非显而易见的约束——添加工程注释:
csharp
/*
    [日期] - [作者]

    [此修复存在的原因以及它能防止的问题。]

    原因:[一句话摘要。]
*/
如果修复不言自明(例如:将
>
改为
>=
),则跳过注释。

2.3 — Verify

2.3 — 验证

Run through this checklist after applying the fix:
  • Re-read every modified file — not just the changed lines, but the surrounding method to confirm the fix fits the context.
  • Public API preserved — did you change a method signature, remove a parameter, or alter return type? If so, check for callers (grep for the method name). Adding an overload is safer than modifying the existing signature.
  • Non-buggy case unaffected — does the fix change behavior for inputs that were already working correctly? Walk through the happy path mentally.
  • Null safety — if you added a null check, does it handle the null case correctly (early return, default value, exception)? If you removed one, are you sure the value can never be null?
  • Cache coherence — if the bug involved cached data, does the fix invalidate the right cache at the right time? Is there a second cache layer you missed?
  • Both layers — if the bug spans C# and TypeScript, did you fix both sides? A C# fix alone may leave the Vue component in an inconsistent state.
  • Run
    /build
    to confirm the fix compiles. Suggest the user run
    /test
    if relevant tests exist.
应用修复后运行以下检查清单:
  • 重新阅读所有修改的文件——不仅是修改的行,还要查看周围的方法,确认修复符合上下文。
  • 公共API保持兼容——你是否修改了方法签名、删除了参数或更改了返回类型?如果是,检查调用者(grep方法名称)。添加重载比修改现有签名更安全。
  • 未受影响的正常场景——修复是否改变了原本正常工作的输入的行为?在脑海中梳理正常流程。
  • Null安全性——如果你添加了null检查,它是否正确处理了null情况(提前返回、默认值、异常)?如果你移除了null检查,你确定值永远不会为null吗?
  • 缓存一致性——如果bug涉及缓存数据,修复是否在正确的时机使正确的缓存失效?你是否遗漏了第二个缓存层?
  • 双层面修复——如果bug涉及C#和TypeScript,你是否修复了两边?仅修复C#可能会让Vue组件处于不一致状态。
  • **运行
    /build
    **确认修复可编译。如果存在相关测试,建议用户运行
    /test

2.4 — Escalate If Needed

2.4 — 必要时升级处理

Some bugs can't be fixed with a code change alone. Stop and tell the user if the fix requires:
  • A database migration — schema change, new column, index, or data backfill. Suggest using
    /migration
    or
    /plugin-migration
    .
  • A SQL data fix — corrupted data in production that needs a script. Suggest using
    /sql
    .
  • A breaking API change — the fix would change a public method signature that plugins depend on. Discuss the deprecation approach with the user.
  • A Rock framework change — the bug is in
    Rock.dll
    core infrastructure, not in a block or service. These changes have a much larger blast radius and need extra review.
  • A cache architecture change — the fix requires changing how or when a cache is populated/invalidated at a systemic level, not just adding a
    FlushItem()
    call.

有些bug无法仅通过代码变更修复。如果修复需要以下操作,请停止并告知用户:
  • 数据库迁移—— schema变更、新列、索引或数据回填。建议使用
    /migration
    /plugin-migration
  • SQL数据修复——生产环境中的损坏数据需要脚本。建议使用
    /sql
  • 破坏性API变更——修复会改变插件依赖的公共方法签名。与用户讨论弃用方案。
  • Rock框架变更——bug存在于
    Rock.dll
    核心基础设施中,而非块或服务。这些变更的影响范围大得多,需要额外审查。
  • 缓存架构变更——修复需要系统性地改变缓存填充/失效的方式或时机,而非仅添加
    FlushItem()
    调用。

Phase 3 — Commit Message

Phase 3 — 提交信息

Output a ready-to-use commit message following Rock's release-note convention.
输出符合Rock发布说明规范的即用型提交信息。

Format

格式

+ (Domain) Fixed [concise description of what was broken]. (Fixes #XXXX)
Rules:
  • Start with
    Fixed
    (past tense) — this classifies it as a Bug Fix in release notes.
  • The description should be readable as a standalone release note — someone scanning the changelog should understand what was wrong without seeing the code.
  • Include
    (Fixes #XXXX)
    only if an issue number was provided in the bug report.
  • Use exactly one domain from the list above.
  • If the fix is trivial (typo, unused import, whitespace) and doesn't warrant a release note, use the
    -
    prefix instead:
    - Fixed typo in variable name.
+ (Domain) Fixed [对问题的简洁描述]. (Fixes #XXXX)
规则:
  • Fixed
    (过去式)开头——这会在发布说明中归类为Bug Fix。
  • 描述应可作为独立的发布说明——浏览变更日志的人无需查看代码就能理解问题所在。
  • 仅当bug报告中提供了问题编号时才包含
    (Fixes #XXXX)
  • 从上述列表中选择恰好一个领域。
  • 如果修复是微不足道的(拼写错误、未使用的导入、空格)且无需发布说明,使用
    -
    前缀:
    - Fixed typo in variable name.

Examples

示例

+ (Core) Fixed the friendly schedule text display for single-date schedules. (Fixes #6694)
+ (Finance) Fixed ACH transactions failing when the gateway returns a null reference number.
+ (CRM) Fixed duplicate detection not considering middle name during merge.
+ (Group) Fixed group member role dropdown not respecting the configured sort order.
- Fixed incorrect variable name in ConnectionRequestService.
+ (Core) Fixed the friendly schedule text display for single-date schedules. (Fixes #6694)
+ (Finance) Fixed ACH transactions failing when the gateway returns a null reference number.
+ (CRM) Fixed duplicate detection not considering middle name during merge.
+ (Group) Fixed group member role dropdown not respecting the configured sort order.
- Fixed incorrect variable name in ConnectionRequestService.

Output

输出

Present the commit message in a code block so the user can copy it directly. If you're uncertain about the domain or description, offer alternatives and let the user choose.

将提交信息放在代码块中,方便用户直接复制。如果你对领域或描述不确定,提供替代方案让用户选择。

Examples

示例

Example 1 — Obvious fix (skip straight to Phase 2):
User says: "The save button on GroupDetail doesn't persist the description field. The description textarea is reading from
bag.name
instead of
bag.description
."
The user identified the exact cause. Go directly to Phase 2 — fix the property reference, re-read the surrounding method, run
/build
, and output the commit message. No need to load reference files or write a root cause summary.
Example 2 — Vague/complex bug (full workflow):
User says: "After merging two people, their giving history disappears."
This needs the full workflow. Load
references/rock-bug-patterns.md
— the "PersonAlias vs Person confusion" pattern is likely relevant. Trace the merge logic to find where
PersonId
is used instead of
PersonAliasId
, confirm by checking the FK relationships, then apply the minimal fix in Phase 2.

示例1 — 明显修复(直接跳到Phase 2):
用户说:“GroupDetail上的保存按钮无法持久化描述字段。描述文本框读取的是
bag.name
而非
bag.description
。”
用户已经明确了确切原因。直接进入Phase 2——修复属性引用,重新阅读周围的方法,运行
/build
,然后输出提交信息。无需加载参考文件或撰写根本原因摘要。
示例2 — 模糊/复杂bug(完整工作流):
用户说:“合并两个人后,他们的捐赠记录消失了。”
这需要完整的工作流。加载
references/rock-bug-patterns.md
——“PersonAlias vs Person混淆”模式可能相关。跟踪合并逻辑,找到使用
PersonId
而非
PersonAliasId
的地方,通过检查FK关系确认,然后在Phase 2中应用最小化修复。

Troubleshooting

故障排除

These are common problems you may hit during the bugfix workflow itself:
Fix doesn't compile. Run
/build
, read the exact error. Common causes: missing
using
statement for a type you referenced, wrong parameter type in a method call, or a property name that doesn't exist on the bag. Fix the compilation error — don't skip the build check.
Root cause unclear after investigation. Widen the search: check
git log --oneline -20
on affected files for recent regressions, grep for other callers of the suspect method, and look one layer up (the bug may be in a caller, not the method itself). If still unclear, ask the user for reproduction steps or specific data conditions that trigger it.
Multiple possible root causes. State all candidates with your reasoning for each. Recommend fixing the most likely one first, and explicitly note the others so the user can investigate further if the first fix doesn't resolve it. Don't silently pick one and ignore the rest.
以下是你在bug修复工作流中可能遇到的常见问题:
修复无法编译。 运行
/build
,读取确切错误。常见原因:引用的类型缺少
using
语句、方法调用中的参数类型错误、bag上不存在的属性名称。修复编译错误——不要跳过构建检查。
调查后根本原因仍不明确。 扩大搜索范围:检查受影响文件的
git log --oneline -20
寻找近期回归,grep可疑方法的其他调用者,查看上一层(bug可能出现在调用者中,而非方法本身)。如果仍不明确,请用户提供复现步骤或触发bug的特定数据条件。
多个可能的根本原因。 列出所有候选原因并说明每个的推理。建议先修复最可能的原因,并明确标注其他原因,以便用户在首次修复未解决问题时进一步调查。不要默默选择一个而忽略其他。