Skip to content

fix: bug 批次修复(PM 派发循环)— #606 #673 #610 #548 #678 #650 已落地 - #677

Merged
yinlianghui merged 16 commits into
mainfrom
claude/hotcrm-bug-triage-ks55ze
Aug 5, 2026
Merged

fix: bug 批次修复(PM 派发循环)— #606 #673 #610 #548 #678 #650 已落地#677
yinlianghui merged 16 commits into
mainfrom
claude/hotcrm-bug-triage-ks55ze

Conversation

@yinlianghui

@yinlianghui yinlianghui commented Aug 5, 2026

Copy link
Copy Markdown
Collaborator

Description

PM 派发循环的 bug 批次修复分支(维护者指定的统一开发分支)。每个 issue 由独立 os-dev agent 在专属 worktree 实现,经文件面互斥调度后串行合入本分支。每条均经 PM 对照远端与 CI 复核,非采信 agent 自述。

已落地:

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation update
  • Code refactoring
  • Performance improvement
  • CI/CD update

Breaking:#548 移除了 app 层 owner 列。升级前必须运行 pnpm backfill:owner(changeset 已写明 FROM→TO)。

Related Issues

Fixes #606
Fixes #673
Fixes #610
Fixes #548
Fixes #678
Fixes #650
Related to #549(待裁定)、#612#620#685#686#694#695objectstack-ai/objectstack#5372、#5378、#5383、#5386

Changes Made

  • 12 个业务对象:移除 owner lookup,显式声明 owner_id(带 system: true);views / pages / highlightFields / 索引 / 数据集 / 仪表盘过滤与钻取 / RLS 谓词 / 4 个语言包 / 导入映射 / hooks / flows 同步改指
  • src/profiles/:allowTransfer 授权轴 + 规范说明
  • scripts/backfill-owner-id.ts:一次性回填(需 --apply 才写)
  • src/actions/:显示名解析块共享化,两个写入方拼接同一份
  • src/flows/:8 处惰性 decision 条件移除,承重注释迁到真正生效的出边上
  • content/docs/analytics/dashboards.mdxcontent/docs/ai-copilot/live-schema*.mdxscripts/wow1-live-schema.sh
  • 新增守卫:test/ownership-model.test.tstest/flow-decision-authority.test.tstest/parent-derived-reach.test.ts,以及 docs-drift / action-sandbox / global-actions 中的多条

Testing

  • Unit tests pass (npm test) — 55 文件 / 1319 通过,1 skipped
  • Linting passes (npm run lint) — 0 errors,warnings 均为 main 既有
  • Build succeeds (npm run build) — ✓
  • Manual testing completed — 每一单都做了反向验证并预先声明预期方向;多处结论由真实 kernel 启动测量得出而非阅读推断
  • New tests added (if applicable)

Screenshots

N/A

Checklist

  • I have added a changeset (pnpm changeset) — required on every PR, or the skip-changeset label is applied
  • My code follows the style guidelines of this project
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes
  • Any dependent changes have been merged and published

⚠️ 待维护者拍板(不阻塞本 PR 已落地部分)

  1. Sharing coverage gap: territory/team-shared accounts show quote, task and contract related lists the rep cannot read #549 — 2026-08-02 决议的前提被实测证伪:controlled_by_parent 的派生不跟随主记录的共享,应用该决议会让报价/合同全组织可读、报价全组织可写。已置 needs-user-decision,OWD 未做任何变更。
  2. Ownership model mismatch: app-authored owner lookups vs the platform's owner_id — reassigning Owner in the UI does not move record access #548 中两处 PM 决定,给否决窗口:(a) owner_id 改为显式声明而非依赖注入(平台作者期校验器不认注入列,已上报 objectstack#5378);(b) service_agent 获得 crm_taskallowTransfer,超出书面决议的「等 escalation-reassignment 落地」—— 不给则客服升级工单整体 403;回退是一行,pin 测试已标明位置。

Additional Notes

同批次分诊产出(不在本 PR 内):关闭前提过期单 #523#575;#620 前提经实测推翻(真实用户路径正常求值,仅 seed 路径告警)已降级 P2;上游阻塞组 13 条保持跟踪。

🤖 Generated with Claude Code

https://claude.ai/code/session_0194QUW67v7vyLToEd9NSeRM

claude and others added 4 commits August 5, 2026 02:16
`sales_copilot` 在 #512 已退役(应用自撰 agent 全部移除,表面改为 skills-only,
见 ADR-0063 §2),#586 清掉了 src/ 下的两处引用,但旗舰 "Wow #1 — live schema"
示范仍在四处把它当作 `POST /api/v1/ai/chat` 的 `agent`:可执行脚本 + 三个语系文档。
这是一条会真正跑挂的路径——`loadAgent()` 拒绝非平台 agent 名,脚本跑在 `curl -fsS`
下,第 2 步直接中断;文档则把同一个 body 当作可复制粘贴的 curl 发出去。

四处统一改为平台 agent `ask`(显式写出而非省略,理由见下),周边行文同步改成真实
架构:HotCRM 的 `live_data` skill 挂载在 `ask` 上,而不是一个应用自撰的 copilot。
三个语系保持同步。objectstack.config.ts 的注释里 "the two agents + skills still
validate" 一并改正——#512 之后这里已没有任何 agent(同时完成 #612 第 2 项)。

test/docs-drift.test.ts 新增守卫:把文档代码块与示范脚本里出现的每个 `agent:` /
`defaultAgent:` 取值,钉在从 spec 读出的平台 agent 集合(`AgentSchema.shape.surface`)
上——与 metadata-references.test.ts 对 `App.defaultAgent` 用的是同一处推导,两者覆盖
同一契约的两个表面。这也是这里显式写 `"ask"` 而非按 ADR-0063 §1 省略该键的原因:
省略会让守卫无输入可查,沦为空转。守卫自带 vacuity 断言,扫不到任何样本即报红。

反向验证(方向为事先预判,两个方向都已跑过):
- 把四处任一改回 `sales_copilot` → 新守卫转红,并逐文件点名(已实测脚本 + 英文档两条)。
- 把四处 `agent` 键全部删除 → 不是静默转绿,而是 vacuity 断言报红。

Refs #606
… the raw id

The activity bodies stamped `ctx.user?.name`, which is NOT a display name on
the dispatch path the Console uses: @objectstack/runtime 17.0.0-rc.2 builds the
REST action context's user as `{ id: ec.userId, name: ec.userId, ... }`
(dist/index.js:5397), so the key is present and carries the id. Every timeline
row written from a record page therefore showed a 32-character id where the
actor's name belongs. The MCP path (dist/index.js:1776) prefers
`ec.userName ?? ec.userDisplayName`, but nothing populates either field, so it
lands on the id as well.

The shared activity body now resolves the name from `sys_user.name` and keeps
the id only as a last resort — an unattributable activity is worse than an ugly
one. A user object already carrying a name different from its id is believed
as-is, so this is one query on the broken path and none on a working one, and
the block deletes itself the day the platform honours `ctx.user.name`.

Tests run the REAL dispatcher user shape (`name === id`), which is why the
pre-existing "carries the acting user" guard stayed green through the whole
bug: the harness default (`{ id: 'usr_1', name: 'Ada Lovelace' }`) is a shape
REST never produces.

Refs #673
@vercel

vercel Bot commented Aug 5, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
hotcrm Ignored Ignored Aug 5, 2026 3:30am

Request Review

@github-actions github-actions Bot added ci/cd CI plumbing and the verification pipeline configuration Build and app configuration files backend Server-side behaviour — hooks, flows, actions labels Aug 5, 2026
@yinlianghui yinlianghui changed the title fix: bug 批次修复(PM 派发循环)— #606 已落地,#548/#673 随后合入本分支 fix: bug 批次修复(PM 派发循环)— #606/#673 已落地,#548/#610 在飞 Aug 5, 2026
claude added 2 commits August 5, 2026 02:32
…uard the list

Refs #610

content/docs/analytics/dashboards.mdx listed tiles that no dashboard
declares — CRM Overview 与 Executive 与真实 metadata 的重合度为零 —
并声称 "Cases Approaching SLA 是本仪表盘点击最多的小部件":该磁贴不存在,
且仓库中没有任何点击遥测,两个前提都是编造的。该说法直接删除,不以另一个
未经测量的最高级替换。

页面改为按当前注册的五个仪表盘逐条描述真实 widget title,其中
sales_activity_dashboard(#592 / PR #670 加入)此前完全没有文档。同时纠正
三处实质性错误:#587 之后任何磁贴都不显示同比趋势;Sales Activity 与
Customer Service 刻意没有日期范围选择器(#460 的 datetime 过滤缺陷);
导出 PDF、周报订阅、阈值告警等本应用并未实现的能力清单被替换为各仪表盘
真正声明的控件。

test/docs-drift.test.ts 新增 dashboards 规则(不改动 #606 的既有规则):
章节内的每个磁贴条目必须解析到该仪表盘的 widget title,正文中的
`**Name** tile` 引用必须解析到某个仪表盘的 widget,且每个已注册的仪表盘
都必须在页面上有章节 —— 最后一条正是第五个仪表盘无文档发布时会触发的。
三处 vacuity 断言保证该规则不会在零输入上变绿。
@yinlianghui yinlianghui changed the title fix: bug 批次修复(PM 派发循环)— #606/#673 已落地,#548/#610 在飞 fix: bug 批次修复(PM 派发循环)— #606/#673/#610 已落地,#548 在飞 Aug 5, 2026
claude added 2 commits August 5, 2026 02:52
…d` (Refs #548)

Every business object authored its own `owner` lookup while the platform kept a
separate `owner_id` beside it. Only `owner_id` was ever read by OWD, sharing
rules, owner-scope widening or the #547 `is_private` row filter — so "Owner" on
a form was a label, not a permission: reassigning it moved the record in every
list, view and report and moved no access at all, records created on someone's
behalf stayed scoped to their creator, and any org design built on the visible
field silently did something else.

Option B per the recorded decision (2026-08-02): drop the app-authored field and
surface the platform column.

- `owner` removed from all 12 objects that carried it (account, campaign, case,
  contact, contract, event, forecast, knowledge_article, lead, opportunity,
  quote, task — `crm_event` included; it was added by #592 after the issue was
  filed). `renewal_owner` / other business-role lookups stay: they are not
  ownership.
- `owner_id` is DECLARED on each of them rather than left to registry injection.
  Injection-only does not work for the surfaces the decision names: `os validate`
  reports `highlightFields: ['owner_id']` as "not a field on this object —
  silently skipped by every consumer" and rejects a CEL predicate reading
  `record.owner_id` outright, because author-time rules resolve fields against
  the authored schema. The platform documents author-declared fields as winning
  over injection; `system: true` keeps the marker its own tooling reads (the
  clone path strips system columns, so a copy is stamped to the cloner instead
  of inheriting — and being denied for — the source's owner). Per-object labels,
  field groups and `trackHistory` survive with it.
- No `defaultValue: cel os.user.id`. The security middleware stamps the acting
  user onto any insert that leaves `owner_id` empty, which is stronger than a
  field default that evaluated to nothing on every user-less write (#620). Those
  boot warnings go with the fields.
- Views, pages, highlight strips, dataset columns, dashboard global filters and
  drill-down columns, RLS predicates, translations (4 locales), import mappings,
  hooks, flows and seeds all repointed. Dataset DIMENSION names stay `owner` —
  they are semantic-layer aliases over the `owner_id` column, and the reports
  and widgets that select by name are unchanged.
- `demo_bootstrap` collapses from a two-column sweep to one. The half-claimed
  state of #622 is now unrepresentable rather than guarded against.
- `allowTransfer` granted deliberately: system_admin (every object),
  sales_manager (exactly the objects it holds modifyAllRecords on),
  service_agent (crm_task only — escalation opens the follow-up task under the
  account owner through `ctx.api`, which the guard does see).

Verification item from the decision, measured rather than assumed
(`test/ownership-model.test.ts`, real ObjectQL): the #3004 guard is an operation
middleware, so which of our writers it can see is not uniform.

  1. a `beforeInsert` hook mutating `input` is INVISIBLE to it — the hook phase
     runs inside the operation the middleware already wrapped. `lead_auto_assign`
     round-robins onto another rep with no transfer grant, and needs none.
  2. a hook's `ctx.api` write is a NEW operation carrying the same context, so
     the guard does see it — this is what the service_agent grant is for.
  3. `runAs: 'system'` flows and seeds short-circuit the middleware entirely:
     no gate, and no auto-stamp either, which is why demo_bootstrap still exists.

Migration: `scripts/backfill-owner-id.ts` (`pnpm backfill:owner`, report-only
without `--apply`) copies the displayed owner onto the enforced one where they
diverge. It talks only to the REST API and MUST run before the upgrade — after
it, the old column is unreadable.

Co-Authored-By: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added dependencies Dependency bumps and lockfile changes metadata Declarative metadata — schema, security posture, UI surfaces labels Aug 5, 2026
claude added 2 commits August 5, 2026 03:06
…or_name

`send_email` stamped `actor_name: ctx.user?.name ?? null` — the byte-identical
defect fixed for the activity family in #673, on the same column. On the
dispatch path the Console uses, `ctx.user.name` is not a display name:
@objectstack/runtime 17.0.0-rc.2 builds the REST action context's user as
`{ id: ec.userId, name: ec.userId, … }` (dist/index.js:5397), so the key is
present and carries the id. Every logged email on a contact's timeline rendered
a 32-character id where the sender's name belongs.

The resolution block is now shared rather than copied: `global.actions.ts`
exports it as body SOURCE TEXT (`ACTOR_NAME_RESOLUTION_SOURCE`) and both bodies
splice it at authoring time. A helper function could not be shared — an action
body ships body-only and runs in QuickJS with no module scope, so a call to an
import is a ReferenceError at runtime; splicing a string while Node builds the
metadata leaves each body self-contained. The generated activity bodies are
byte-identical to before apart from two comment lines.

Tests execute `send_email` under the REAL QuickJS harness with the dispatcher's
own user shape (`name === id`) — the shape that kept the pre-existing
`send_email` guard green through the whole bug, since the harness default
(`{ id: 'usr_1', name: 'Ada Lovelace' }`) is one REST never produces — and pin
that the value is NOT the raw id, that `sys_user` is read once and only when the
dispatcher delivered no name, that a missing or throwing read falls back to the
id instead of blanking the actor, and that no `actor_name` writer stops splicing
the shared block.

Refs #678
claude added 2 commits August 5, 2026 03:14
引擎只在三处求值流程条件:start 节点的 config.condition、decision 节点的
复数 config.conditions[]、以及每条出边的 condition。decision 节点上的单数
config.condition 不被任何 reader 读取:spec 的 DecisionConfigSchema 只声明
conditions 一个键,而 decision 不发布 descriptor configSchema,因此引擎针对
未声明配置键的拒绝检查(#4277)整类豁免它——任何一层都不会报错。

本仓库 8 个 decision 节点(campaign_enrollment、contract_renewal、
forecast_snapshot、opportunity_stagnation)如此声明。行为一直正确只因谓词被
复制到出边,且无任何机制强制同步:单独修改节点副本,走向不变且无声。

选择出边权威(2b)而非节点权威(2a),依据实测:8 个站点中 7 个是单出边
"跳过"闸门,假分支无处可去。节点声明 conditions[] 并去掉出边谓词后,条件为
假时执行器返回 branchLabel 'default',traverseNext 找不到认领该 label 或
isDefault 的出边,按设计回退为求值全部出边,而仅存的出边已无条件——闸门被
静默反转。要使其安全需为每个闸门臆造空节点作 isDefault 汇点。

新增 test/flow-decision-authority.test.ts:静态扫描(含 loop 体嵌套节点)+
真实引擎上的两个可执行证明(惰性证明、失效为放行的实测)。

flow-variable-conditions.test.ts 的 "guard the guard" 断言原先要求至少存在
一个节点级 config.condition 站点——该断言只在缺陷存在时成立,且与该文件自身
注释矛盾,改为断言其为空。

Refs #650
claude added 2 commits August 5, 2026 03:19
…achable by (Refs #549)

#549's recorded decision converts crm_quote and crm_contract to
controlled_by_parent under crm_account so a territory-shared account carries its
quotes and contracts. Before changing an OWD, measure what the engine does with
one: this boots the shipped enforcement stack (ObjectQL + plugin-security +
plugin-sharing over the app's own metadata), materialises the territory rule and
asks, as a real sales_rep, what comes back.

It does not follow the parent's sharing. A rep who can read exactly one account
reads BOTH accounts' contacts, and both quotes' line items while able to read
neither quote: computeControlledByParentFilter resolves the master id set through
the master's RLS policies (this app authors none on crm_account) under a system
context, so ownership and sys_record_share grants are never folded in. With the
proposed conversion applied the same rep saw every quote and every contract in
the org, and could edit a quote on an account they cannot read.

So no OWD changes here. The measurement is pinned as a test, the coverage
ledger's 'derived' entry now says what derived really means, and the shipped
over-reach on contacts and line items is filed as #694.
@yinlianghui yinlianghui changed the title fix: bug 批次修复(PM 派发循环)— #606/#673/#610 已落地,#548 在飞 fix: bug 批次修复(PM 派发循环)— #606 #673 #610 #548 #678 #650 已落地 Aug 5, 2026
claude added 2 commits August 5, 2026 03:30
…erage on #548's owner_id keys

`main` advanced by 6809624 (#679), which completed en / es-ES / ja-JP on every
translatable surface and retired the PENDING_SELECT_LABELS exemption ledger. The
branch carries #548, which removed the app-authored `owner` lookup from all 12
objects that had it and declares the platform's `owner_id` instead — including in
all four locale packs. Both sides edited the same field-map lines, so en.ts,
es-ES.ts and ja-JP.ts conflicted (zh-CN.ts did not: #679 never touched it).

Resolution keeps both, and neither side's edit is a superset of the other:

- FROM main, the whole of #679's content: the 133 (en) / 355 (es-ES, ja-JP) added
  keys, the `pages` group that was absent from three bundles, the shared
  `leadSourceOptions` / `relatedToTypeOptions` spreads in ja-JP, and the label
  corrections the sweep found — notably `crm_account.owner` in ja-JP, which read
  取引先責任者 (the object label for `crm_contact`) and is now 取引先所有者, and
  `crm_contact.owner`, now 取引先責任者の所有者. The branch had carried the old
  strings through its rename, so taking the branch's line would have silently
  reverted two of the drift fixes #679 shipped.
- FROM the branch, the field KEY only: the 12 `owner:` entries per locale become
  `owner_id:`. An entry still keyed `owner` names a field no object declares any
  more — dead metadata that reads as coverage while the screen stays English, and
  exactly what `metadata-references.test.ts` "translated object and field keys
  name real objects and fields" exists to reject.

Applied as `^\s+owner: {` only, so the business-role lookups that are NOT
ownership keep their own keys: `crm_account.renewal_owner`,
`crm_product.product_manager`, the zh-CN `ownership` field-group key, and the
`pipeline_by_owner` / `open_pipeline_by_owner` / `win_rate_by_owner` report keys
are all untouched. A blanket rename would have produced `renewal_owner_id`.

test/metadata-references.test.ts took main's rewrite unchanged — the branch never
edited it — and needed no adjustment: its locale guards derive field names from
the loaded `objectstack.config` rather than hard-coding any, so they now assert
the merged invariant (`owner_id` translated in all four locales) on their own.
No assertion was weakened.

Measured, not assumed — flattened leaf keys per pack, origin/main vs merged:
en 1156 → 1156, es-ES 1216 → 1216, ja-JP 1216 → 1216, zh-CN 1280 → 1280, with the
key-set delta being exactly the 12 `fields.owner.label` → `fields.owner_id.label`
renames in each and nothing else.

`pnpm verify` clean: validate ✓, tsc --noEmit ✓, lint 13 warning(s) (the same 13
#679 left, none i18n), hygiene ✓, build ✓, 55 test files / 1317 tests passed.

Copy link
Copy Markdown
Collaborator Author

🔀 交接说明(给接手本批次的人 / 另一个 Claude 账户的会话)

本批次由 PM 派发循环完成,会话 session_0194QUW67v7vyLToEd9NSeRM该会话即将停止,以下是接手所需的全部状态。所有状态都在 GitHub 上,无本地残留(工作区干净、无未推送提交、无遗留 worktree)。

一、这个 PR 的处置

mergeable_state: clean,16 个提交,CI 全绿(CI / E2E / Code Quality / Changeset / Link Check / CodeQL)。它是 draft,需要人转 ready 并合并。

合并时会由正文的 Fixes自动关闭 6 个 issue:#606#673#610#548#678#650。这是预期行为,不是意外。

⚠️ 合并前必读:#548 是 breaking —— 移除了 app 层 owner 列。升级既有部署前必须先跑 pnpm backfill:owner(不带 --apply 是只读报告)。changeset 里写了 FROM→TO。

二、本仓在本批次采用的分支约定(与默认不同,务必遵守)

  • 所有工作合入单一会话分支 claude/hotcrm-bug-triage-ks55ze,由维护者指定。⛔ 不要为每个 issue 另开 claude/issue-<n>-<slug> 分支 —— 那是本仓的默认习惯,但本批次不用。
  • 并发 agent 通过 flock /tmp/hotcrm-branch-push.lock 串行合入,git merge --no-ff,⛔ 禁 rebase、禁 force-push。
  • 提交信息用 Refs #N,⛔ 绝不用 Fixes/Closes —— 提交信息里的关闭关键字会随合并自动关 issue,改 PR 描述拦不住([CRM] 9 个孤儿 __search 列待清理(17.0 收紧搜索伴生列供给条件后遗留) #528 就是这么被误关的)。Fixes 只写在 PR 正文里。
  • 每个改动配一个 .changeset/*.md,否则 Check Changeset 变红。

三、⚠️ 唯一待维护者拍板的事:#549

2026-08-02 那条已拍板决议所依赖的前提被实测证伪。 controlled_by_parent 的派生不跟随主记录的共享,照决议落地会让报价/合同全组织可读、报价全组织可写。dev 已按纪律停手并回退全部 OWD 变更,只留下测量守卫。

四个选项与两轴分析在 #549 的评论里,已置 needs-user-decisionPM 建议 C(推迟 OWD、修上游生产者 objectstack#5386),必要时以 B 兜底,不要落 A。 这是产品/机密性边界决策,接手者不应自行决定

四、⚠️ 两处 PM 已决、留给维护者的否决窗口(都在 #548)

  1. owner_id 改为显式声明而非依赖平台注入 —— 平台作者期校验器看不见注入列(已上报 objectstack#5378)。上游修好后可回退。
  2. service_agent 获得 crm_taskallowTransfer —— 超出书面决议的「等 escalation-reassignment 落地再给」。不给则客服升级工单整体 403。回退是一行,pin 测试位置在 test/ownership-model.test.ts →「service_agent may assign a TASK and nothing else」。

五、队列现状(标签即状态,可直接接着跑)

Issue 状态 备注
#549 needs-user-decision 等裁定,不可派发
#620 P2,未认领 前提已被实测推翻;阻塞于本 PR 合并后复核启动告警归零即可关闭
#694 upstream:objectstack 修法在平台(#5386);本仓侧的三处文档纠偏建议等 #549 裁定后一并做
#685 pm:queue,未认领 中文 dashboards 页漂移。本批次因维护者限定「排除国际化」而未派发,Blocked-by #610(需本 PR 合并后再译)
#686 / #695 finding 待发现分诊轮
上游跟踪组 13 条 保持 open #664 #656 #567 #528 #526 #525 #524 #522 #521 #520 #510 #509 #508 —— 本仓侧无可修项

本批次新提的上游单:objectstack #5372#5378#5383#5386(均已查重,无重复)。

六、跨账户协作提示

上表中被 assign 的 issue 都是已完成待合并,不是在飞 —— 接手者按「assignee 不是自己 ⇒ 不要碰」处理即可,不会漏活。真正可接的活是第五节里未认领的那几条。

main 移动很快(本批次期间 main 就前进了一次并造成语言包冲突)。基于本分支开新 worktree 前先 git fetch,并注意:src/translations/*.ts 是本仓的冲突热点,解决时要整体采用 main 的译文再重新施加键改动 —— 直觉上的「保留我方」会静默回退掉 main 刚修的翻译(本次就险些发生)。


Generated by Claude Code

@yinlianghui
yinlianghui marked this pull request as ready for review August 5, 2026 07:19
@yinlianghui
yinlianghui added this pull request to the merge queue Aug 5, 2026
Merged via the queue into main with commit 9f748ab Aug 5, 2026
10 checks passed
This was referenced Aug 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment