Skip to content

Commit 8e26d14

Browse files
wehosHongzhi Wenclaude
authored
refactor(plugin-sdk): push_message v2 — visibility/ai_behavior 双轴 + parts 列表 (#1046)
* refactor(plugin-sdk): push_message v2 — visibility/ai_behavior 双轴 + parts 列表 把 push_message 从 message_type+delivery+content 单轴枚举重写成两条正交轴 (visibility / ai_behavior) + OpenAI 风格 parts 列表,让"用户看到哪儿"和 "AI 怎么处理"分开表达,并支持 text+media 同帧推送(game agent 截图流场景)。 新签名(plugin/sdk/shared/core/push_message_schema.py 单一权威): ctx.push_message( visibility=[], # ["chat"] / ["hud"] / [] / 组合 ai_behavior="respond", # respond / read / blind parts=[ {"type":"text","text":...}, {"type":"image","data":bytes,"mime":...}, {"type":"image","url":...}, {"type":"audio","data":bytes,"mime":...}, {"type":"video","url":...}, {"type":"ui_action","action":"media_play_url",...}, {"type":"ui_action","action":"media_allowlist_add","domains":[...]}, ], source=..., target_lanlan=..., metadata=..., priority=..., ) 兼容窗口(v0.9 移除) message_type / delivery / reply / content / binary_data / binary_url / mime / description / unsafe 全部仍可用,每用一个 emit DeprecationWarning。 translate_push_message 在 SDK 层做 v1→v2 翻译,wire payload 同时填新旧 字段,下游 query_service 等未迁移的 reader 透明继续工作。 直接删除(无 compat) SdkContext.register_music_domains() —— 全仓 grep 没有 in-tree 调用。 下游链路 proactive_bridge 重写:读 v2 字段,按 parts 分流到既有 legacy event_types (proactive_message / music_play_url / music_allowlist_add),media 挂在 proactive_message.media_parts,main_server 分发时 base64 解码后调 session.send_media_input 注入到 realtime session。 迁移 bilibili_danmaku / memo_reminder / sts2_autoplay 三个 in-tree 插件已迁到新 签名,零 warning 触发;PLUGIN_DEVELOPMENT_GUIDE.md 全节重写; docs/changelog/plugin-push-message-v2.md 详述动机 / 对照表 / 移除清单。 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(push-message-v2): address PR review — stream_image API, compat fields, target_lanlan 修了首轮 review 提到的 7 个真实问题(一处 reviewer 误判已在 thread 反驳): - main_server.py: 媒体注入用错了 API。realtime client 只暴露 stream_image (image_b64) 和 stream_audio (PCM bytes),不存在 send_media_input。改成 调 stream_image 并直接传 base64 字符串;audio/video 现阶段无对应通道, log warning 后丢弃。URL-only 媒体也改成 warn-and-drop(v0.9 引入 fetch worker 时再补上)。 - _types/protocols.py: push_message Protocol 改成 keyword-only,跟 sdk/shared/core/{context,types}.py 的实现一致,避免 type checker 通过 + 运行时 TypeError 的分裂。 - core/context.py 兼容层:现在也把 binary_data (bytes)、mime、delivery、 reply 落到 wire payload 顶层,确保 query_service 等还没迁移到 v2 的 reader 在 deprecation window 里能继续读到完整 v1 形状。binary_data 从 parts 的 binary_base64 反解出来(仅取首个 image/audio/video part)。 - proactive_bridge.py: 删 SCHEMA_VERSION 未用 import;result_parser fallback 的 except 加 logger.debug 不再静默吞错;music_play_url / music_allowlist_add 事件多带 lanlan_name=target_lanlan 字段,让 plugin 显式 target_lanlan 时 main_server 能路由到指定 session(之前会退化广播)。 - bilibili_danmaku: _push_to_ai 把 self._target_lanlan 透传给 push_message (set_target_lanlan 入口配置的 AI 终于真正生效)。 - PLUGIN_DEVELOPMENT_GUIDE.md: 把失效的 #push_message_kwargs---object 链接 指向新加的 <a id="push-message-v2"></a> 锚点。 未采纳 - _synthesize_legacy_message_type 的 visibility 校验:(visibility=[], ai_behavior=respond) 是 v2 最常用的 "让 AI 转述" 模式,对应的 legacy discriminator 就是 proactive_notification。加 visibility 检查会让这个 最常见的 case 落到 "text",反而是退化。已在 thread 反驳。 - bilibili 的 target_lanlan 据说"重构丢了"——其实重构前这个字段也没在传, 是 pre-existing missing;不过修起来零成本,顺手补上了。 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * docs(plugin-guide): 标注 v2 push_message media parts 的当前实现限制 CodeRabbit 指出文档把 image/audio/video 写得像三种都能直接喂给 AI,但 main_server 这一版只接 inline image (走 stream_image),URL 形态和 audio/video 都会 warn-drop。补一段「当前实现限制」明确说明,并把上面 example 里的 image-url / audio / video 三行改成注释占位,避免插件作者 照抄后发现 AI 实际看不到。 Refs: PR #1046 review thread r3168... * docs(plugin-guide): 迁移表显式标 ai_behavior="blind" CodeRabbit nit:music_* 两条迁移行只写 parts + visibility,读者 可能以为保留默认 respond 也行。补 ai_behavior="blind" 跟例 4/6 和 proactive_bridge fan-out 的语义对齐。 Refs: PR #1046 review thread * lint(docs): forbid relative-up markdown links inside docs/ docs/ 走 VitePress,site root 就是 docs/ 自己。任何 markdown 链接以 \`..\` 开头都会在 deploy 时解析到 docs 外面、构建直接爆炸——这次 changelog 的 \`../../plugin/sdk/...\` 是第二次(?)触发同一个坑。 加 lint 把这条钉死,下次写错文档时 CI 直接拦下,不再让它跑到 main 触发 docs.yml 的 deploy 失败。 新加的 lint - scripts/check_docs_no_relative_paths.py 扫 docs/**/*.md,匹配 \`](...\` 形态的 markdown 链接;任何 target 以 \`..\` 开头都报错 - 不抓 fenced code block 里的 \`cd ../..\` 这类 shell 字面量 (那不是 markdown 链接,对 VitePress 无害) - 没有 per-line 跳过——加了就把这条 lint 的存在意义瓦解了 - analyze.yml 新增一步运行该脚本,job 名字也加 docs-no-relative-paths 修了的 5 份文档(共 52 处链接) - docs/changelog/plugin-push-message-v2.md:上一轮我自己加的 3 处 (../../plugin/...) 改成 inline code - docs/design/home-tutorial-yui-guide-{main-owner,performance-owner, preparation-freeze,three-person-collaboration}-stage-breakdown.md: pre-existing 的 ../../static/*.js 共 49 处,统一去掉链接外壳改成 \`static/foo.js\` 形态——同时移除 changelog 文档里残留的过期描述 send_media_input → stream_image。 * fix(docs-lint): 跳过 fenced code block;细化 changelog video 描述 CodeRabbit 两条 review: 1. scripts/check_docs_no_relative_paths.py 没跳 ```/~~~ 围起来的 fenced code block,文档里写"反面示例"会被自己拦下。加个简单的 fence 状态机,遇到 ``` / ~~~ 切 in_fence,跳过其中所有 link 匹配。 测过:fenced 内 [bad](../foo.md) 不再报,fenced 外的照旧报。 2. changelog 迁移表写 "(or audio/video)" 容易让人以为 video.data 也 被 AI 注入消费。其实 schema 接受任意 image/audio/video 的 data 字段,但 main_server 这版只 stream_image 走通了,audio/video 都 warn-drop。明确写成 "or audio; video accepted in schema but main_server warn-drops it for now" 跟 PLUGIN_DEVELOPMENT_GUIDE 的 "当前实现限制"段对齐。 Refs: PR #1046 review threads * chore(push-message-v2): TODO(v0.9) 标记 description 字段全部清理点 description 在 v2 schema 里没有任何语义角色——只是 v1 通道里给 log line 和 query_service 当 human label 的兜底。当前留着完全是 为了 v1 兼容窗口。本提交把所有还在 surface description 的位置打 TODO(v0.9) 标记,方便 v0.9 cleanup PR 一次 grep 全删: - plugin/core/context.py: legacy_description 合成处 - plugin/plugins/{bilibili_danmaku,memo_reminder,sts2_autoplay}: metadata={"description": ...} 三处(迁移时顺手留了,但其实 metadata.description 也没人读) - plugin/server/application/messages/query_service.py: 序列化 response 时把 record.get("description") 反弹回去那一处 - docs/changelog/plugin-push-message-v2.md: 在 "Removed in v0.9" 列表里单独点名 description,列出全部 TODO(v0.9) 位置方便定位 行为零变化:description 仍按现状走完整 v1 通道;只是现在 v0.9 cleanup PR 可以 \`grep "TODO(v0.9)"\` 全找出来。 * fix(push-message-v2): legacy description 顶层从 metadata.description 兜底 CodeRabbit 指出:上一 commit 给 3 个迁移插件加 TODO(v0.9) 时,把 description 放进了 metadata["description"],但 _build_wire_payload 里 legacy_description 只看 kwarg、没看 metadata。结果 v2 调用方推的 消息到 query_service / 老日志那一头会提前丢标签——deprecation window 内不该提前失效。 补三档优先级回填: 1. 显式 description= kwarg(v1 调用方) 2. metadata["description"](已迁到 v2、把 label 放 metadata 里的) 3. 空串(纯 v2 native,从不设 label) 行为:v2 迁移过的 plugin 走 #2,wire payload 顶层 description 不再 丢;v0.9 cleanup 时三档一起删(TODO 注释已更新)。 Refs: PR #1046 review thread --------- Co-authored-by: Hongzhi Wen <cartabio.coder1@gmail.com> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 91f8a78 commit 8e26d14

21 files changed

Lines changed: 1425 additions & 444 deletions

.github/workflows/analyze.yml

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ permissions:
1313

1414
jobs:
1515
python-lint:
16-
name: Python lint (ruff + async-blocking + no-loguru + no-temperature + prompt-hygiene + i18n-sync)
16+
name: Python lint (ruff + async-blocking + no-loguru + no-temperature + prompt-hygiene + i18n-sync + docs-no-relative-paths)
1717
runs-on: ubuntu-latest
1818
timeout-minutes: 10
1919
steps:
@@ -88,3 +88,12 @@ jobs:
8888
# there's nothing to diff against.
8989
if: github.event_name == 'pull_request'
9090
run: python scripts/check_i18n_sync.py --base "origin/${{ github.base_ref }}"
91+
92+
- name: Forbid relative-up markdown links inside docs/
93+
# docs/ ships through VitePress with itself as the deploy root;
94+
# any markdown link target starting with '..' resolves outside the
95+
# site and breaks deploy. We've shipped this regression more than
96+
# once — this check fails the build before the next attempt lands.
97+
# Fix is to inline the path as code (`foo/bar.js`) instead of a
98+
# link, or move the referenced content into docs/.
99+
run: python scripts/check_docs_no_relative_paths.py
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
# Plugin SDK: `push_message` v2 (orthogonal axes + parts)
2+
3+
**Status**: introduced this release · old fields scheduled to be removed in **v0.9**.
4+
5+
## Summary
6+
7+
`ctx.push_message` now uses two orthogonal axes plus an OpenAI-style `parts`
8+
list, replacing the conflated `message_type` + `delivery` + `content` /
9+
`binary_data` / `binary_url` legacy shape.
10+
11+
```python
12+
ctx.push_message(
13+
visibility=[], # ["chat"] / ["hud"] / both / [] (default)
14+
ai_behavior="respond", # respond / read / blind
15+
parts=[
16+
{"type": "text", "text": "..."},
17+
{"type": "image", "data": img_bytes, "mime": "image/png"},
18+
{"type": "image", "url": "https://..."},
19+
{"type": "audio", "data": ..., "mime": "audio/mpeg"},
20+
{"type": "video", "url": "..."},
21+
{"type": "ui_action", "action": "media_play_url", "url": "..."},
22+
{"type": "ui_action", "action": "media_allowlist_add", "domains": [...]},
23+
],
24+
source="my_plugin",
25+
target_lanlan="",
26+
metadata={...},
27+
priority=0,
28+
)
29+
```
30+
31+
* **`visibility`** = where the user sees the plugin's parts rendered
32+
*verbatim* (independent of AI). `[]` means "user does not see the
33+
parts directly; if AI replies, only the AI's bubble is visible".
34+
* **`ai_behavior`** = how the LLM treats the parts (`respond` triggers a
35+
turn now, `read` ingests context for natural mention later, `blind`
36+
bypasses the LLM entirely).
37+
* **`parts`** = ordered content list. `data: bytes` is base64-encoded by
38+
the SDK adapter for the wire.
39+
40+
Schema source-of-truth:
41+
`plugin/sdk/shared/core/push_message_schema.py`.
42+
43+
## Why
44+
45+
The previous `push_message` had three problems we kept hitting:
46+
47+
1. **`message_type` overloaded routing + content shape** — every new use
48+
case (`proactive_notification`, `music_play_url`, `music_allowlist_add`,
49+
the proposed `media_inject`, …) needed a new enum value and a new
50+
`if msg_type == ...` branch in `proactive_bridge.py`. Two distinct
51+
axes (where it goes vs. what it carries) collapsed onto a single
52+
discriminator.
53+
2. **`delivery` (`proactive` / `passive` / `silent`) implicitly bundled
54+
"AI engagement" with "user visibility"**`silent` meant "no LLM AND
55+
HUD-only", which left no slot for "feed AI context but don't trigger
56+
a turn" (game agent screenshot streaming) or "render plugin's verbatim
57+
chat bubble without AI noticing" (music card today).
58+
3. **`content` / `binary_data` / `binary_url` were one-of-three** — no
59+
way to send `text + image` together. Plugins that wanted a system
60+
prompt with an attached screenshot needed two separate
61+
`push_message` calls and hoped the order survived.
62+
63+
The new schema solves these by:
64+
65+
* dropping `message_type` entirely (use `parts[*].type` for content
66+
shape; use `visibility` + `ai_behavior` for routing);
67+
* splitting `delivery` into two **truly orthogonal** axes — `visibility`
68+
and `ai_behavior` — that capture all 12 combinations the old single
69+
`delivery` enum couldn't express;
70+
* using `parts: list[dict]` so a single push can carry text + media in
71+
any order.
72+
73+
## Migration cheat sheet
74+
75+
| Old | New |
76+
|---|---|
77+
| `message_type="proactive_notification"` (default) | drop the field; defaults are `visibility=[], ai_behavior="respond"` |
78+
| `delivery="proactive"` / `reply=True` | default — drop |
79+
| `delivery="passive"` | `ai_behavior="read"` |
80+
| `delivery="silent"` / `reply=False` | `visibility=["hud"], ai_behavior="blind"` |
81+
| `content="X"` | `parts=[{"type":"text","text":"X"}]` |
82+
| `binary_data=bytes, mime=...` | `parts=[{"type":"image","data":bytes,"mime":...}]` (or `audio`; `video` accepted in schema but main_server warn-drops it for now) |
83+
| `binary_url=URL` | `parts=[{"type":"image","url":URL}]` |
84+
| `message_type="music_play_url"` | `parts=[{"type":"ui_action","action":"media_play_url","url":..., "media_type":"audio"}]`, `visibility=["chat"]`, `ai_behavior="blind"` |
85+
| `message_type="music_allowlist_add"` | `parts=[{"type":"ui_action","action":"media_allowlist_add","domains":[...]}]`, `ai_behavior="blind"` |
86+
| `register_music_domains(domains)` SDK helper | **deleted** — push directly via `ui_action: media_allowlist_add` (see above) |
87+
| `description="X"` | `metadata={"description": "X"}` |
88+
| `unsafe=True` | drop |
89+
90+
## Backward compatibility
91+
92+
All legacy parameters (`message_type`, `description`, `content`,
93+
`binary_data`, `binary_url`, `mime`, `delivery`, `reply`, `unsafe`) still
94+
work and are translated client-side by
95+
`translate_push_message`.
96+
Each legacy parameter that is actually passed emits a `DeprecationWarning`
97+
on every call, citing this version target.
98+
99+
The wire payload populates **both** v2 (`schema`, `visibility`,
100+
`ai_behavior`, `parts`) and synthesised legacy fields (`message_type`,
101+
`content`, `binary_url`, `description`) so that downstream readers that
102+
have not migrated yet (notably
103+
`plugin/server/application/messages/query_service.py`)
104+
keep working through the deprecation window.
105+
106+
`SdkContext.register_music_domains()` is **removed outright** — no
107+
in-tree consumers were using it. Plugins that called it must migrate
108+
to the `ui_action: media_allowlist_add` part shape.
109+
110+
## Removed in v0.9
111+
112+
* All legacy `push_message` parameters listed above.
113+
* The legacy fields synthesised on the wire payload (`message_type`,
114+
`content`, `binary_data`, `binary_url`, `description`, `unsafe`,
115+
`delivery`, `reply`).
116+
* `description` everywhere it currently lingers — has no semantic
117+
consumer in v2, only surfaces as a human label in legacy log lines and
118+
the `query_service` response. Marked with `TODO(v0.9)` in
119+
`plugin/core/context.py`, `plugin/server/application/messages/query_service.py`,
120+
and the three migrated in-tree plugin senders
121+
(`bilibili_danmaku` / `memo_reminder` / `sts2_autoplay`) so the cleanup
122+
PR can grep for the marker.
123+
* The legacy event-bus event shape (`proactive_message` event type
124+
itself stays, but its `media_parts` / `visibility` / `ai_behavior`
125+
fields become the only schema; `delivery_mode` becomes derived).
126+
127+
## Touched files (this release)
128+
129+
* `plugin/sdk/shared/core/push_message_schema.py` (new)
130+
* `plugin/sdk/shared/core/context.py`, `types.py`
131+
* `plugin/sdk/plugin/base.py` (deleted `register_music_domains`)
132+
* `plugin/_types/protocols.py`, `_types/models.py`
133+
* `plugin/core/context.py`
134+
* `plugin/server/messaging/proactive_bridge.py`
135+
* `main_server.py` (image `media_parts``session.stream_image`; audio/video warn-drop pending a transport)
136+
* `plugin/plugins/{bilibili_danmaku,memo_reminder,sts2_autoplay}/__init__.py` (migrated senders)
137+
* `plugin/PLUGIN_DEVELOPMENT_GUIDE.md`

docs/design/home-tutorial-yui-guide-main-owner-stage-breakdown.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -51,15 +51,15 @@
5151

5252
主负责人长期持有的重点文件建议为:
5353

54-
- [static/universal-tutorial-manager.js](../../static/universal-tutorial-manager.js)
54+
- `static/universal-tutorial-manager.js`
5555
- `templates/index.html`
5656
- `static/yui-guide-steps.js`
5757
- 本文档与相关设计文档
5858

5959
主负责人可以在联调时触达,但不应长期主写的文件:
6060

61-
- [static/app-ui.js](../../static/app-ui.js)
62-
- [static/app-buttons.js](../../static/app-buttons.js)
61+
- `static/app-ui.js`
62+
- `static/app-buttons.js`
6363

6464
主负责人不应主动承包的内容:
6565

@@ -101,7 +101,7 @@
101101
- 冻结 `YuiGuideStep` 的字段形状
102102
- 明确 `Director` 最小挂接接口
103103
- 明确 `handoff` 最小字段结构
104-
- 明确以下高冲突文件的 owner:[static/universal-tutorial-manager.js](../../static/universal-tutorial-manager.js)`templates/index.html``static/yui-guide-steps.js`
104+
- 明确以下高冲突文件的 owner:`static/universal-tutorial-manager.js``templates/index.html``static/yui-guide-steps.js`
105105
- 约定合并顺序和合并窗口
106106

107107
### 这一阶段主负责人应该产出的东西
@@ -138,7 +138,7 @@
138138

139139
### 主负责人此阶段的核心职责
140140

141-
-[static/universal-tutorial-manager.js](../../static/universal-tutorial-manager.js) 中补出 step change 到 Director 的挂接点
141+
-`static/universal-tutorial-manager.js` 中补出 step change 到 Director 的挂接点
142142
-`templates/index.html` 中补脚本和样式装配位
143143
- 建立 `static/yui-guide-steps.js` 首版骨架
144144
-`intro_basic``intro_proactive``intro_cat_paw` 这些场景先收口进同一套场景注册表

docs/design/home-tutorial-yui-guide-performance-owner-stage-breakdown.md

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,12 @@
2727

2828
同时还核对了当前代码中的真实锚点与运行时状态:
2929

30-
- [static/universal-tutorial-manager.js](../../static/universal-tutorial-manager.js)
31-
- [static/yui-guide-steps.js](../../static/yui-guide-steps.js)
30+
- `static/universal-tutorial-manager.js`
31+
- `static/yui-guide-steps.js`
3232
- `templates/index.html`
33-
- [static/avatar-ui-popup.js](../../static/avatar-ui-popup.js)
34-
- [static/app-interpage.js](../../static/app-interpage.js)
35-
- [static/live2d-emotion.js](../../static/live2d-emotion.js)
33+
- `static/avatar-ui-popup.js`
34+
- `static/app-interpage.js`
35+
- `static/live2d-emotion.js`
3636
- `templates/viewer.html`
3737

3838
---
@@ -43,7 +43,7 @@
4343

4444
当前已经成立的前置条件有:
4545

46-
- 首页已经加载了 [static/yui-guide-steps.js](../../static/yui-guide-steps.js)[static/universal-tutorial-manager.js](../../static/universal-tutorial-manager.js)
46+
- 首页已经加载了 `static/yui-guide-steps.js``static/universal-tutorial-manager.js`
4747
- `UniversalTutorialManager` 已经具备 `Yui Guide` 运行时桥接能力
4848
- 首页旧教程 step 已经补了首批 `yuiGuideSceneId`
4949
- 共享场景注册表已经存在,并且首页 `sceneOrder.home` 已冻结
@@ -109,7 +109,7 @@
109109

110110
### 5.2 可参与但不应主改
111111

112-
- [static/yui-guide-steps.js](../../static/yui-guide-steps.js)
112+
- `static/yui-guide-steps.js`
113113

114114
开发 B 在这个文件里主要补:
115115

@@ -126,12 +126,12 @@
126126

127127
### 5.3 不应直接长期占用
128128

129-
- [static/universal-tutorial-manager.js](../../static/universal-tutorial-manager.js)
129+
- `static/universal-tutorial-manager.js`
130130
- `templates/index.html`
131-
- [static/app-ui.js](../../static/app-ui.js)
132-
- [static/app-buttons.js](../../static/app-buttons.js)
133-
- [static/avatar-ui-popup.js](../../static/avatar-ui-popup.js)
134-
- [static/app-interpage.js](../../static/app-interpage.js)
131+
- `static/app-ui.js`
132+
- `static/app-buttons.js`
133+
- `static/avatar-ui-popup.js`
134+
- `static/app-interpage.js`
135135

136136
如果需要这些文件提供新挂接点,先提接口需求,再由主负责人或开发 C 收口。
137137

@@ -273,7 +273,7 @@ interface YuiGuideDirector {
273273
- 约定 overlay 根节点命名和销毁方式
274274
- 约定 bubble、voice、emotion、cursor 的编排顺序
275275
- 确认所有退出路径最终都汇聚到一次 `destroy()`
276-
- 审视 [static/yui-guide-steps.js](../../static/yui-guide-steps.js) 里的首版 `performance` 是否足够驱动第一阶段
276+
- 审视 `static/yui-guide-steps.js` 里的首版 `performance` 是否足够驱动第一阶段
277277

278278
此阶段不要做的事情:
279279

@@ -438,7 +438,7 @@ interface YuiGuideDirector {
438438

439439
## 10.3 Emotion
440440

441-
从当前仓库看,首页已经加载了 [static/live2d-emotion.js](../../static/live2d-emotion.js),而视图侧已有现成情感调用入口。
441+
从当前仓库看,首页已经加载了 `static/live2d-emotion.js`,而视图侧已有现成情感调用入口。
442442

443443
因此建议开发 B:
444444

docs/design/home-tutorial-yui-guide-preparation-freeze.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -265,18 +265,18 @@ type YuiGuideHandoffToken = {
265265

266266
| 文件 / 模块 | owner | 说明 |
267267
|---|---|---|
268-
| [static/universal-tutorial-manager.js](../../static/universal-tutorial-manager.js) | 主负责人 | 高冲突骨架文件 |
268+
| `static/universal-tutorial-manager.js` | 主负责人 | 高冲突骨架文件 |
269269
| `templates/index.html` | 主负责人 | 首页脚本装配入口 |
270270
| `static/yui-guide-steps.js` | 主负责人 | 共享场景注册表与步骤契约 |
271271
| `static/yui-guide-director.js` | 开发 B | 演出层主模块 |
272272
| `static/yui-guide-overlay.js` | 开发 B | 演出层 UI 壳 |
273273
| `static/css/yui-guide.css` | 开发 B | 演出样式 |
274274
| `static/assets/tutorial/` | 开发 B | 演出素材目录 |
275-
| [static/avatar-ui-popup.js](../../static/avatar-ui-popup.js) | 开发 C | 首页设置入口与菜单结构 |
276-
| [static/app-interpage.js](../../static/app-interpage.js) | 开发 C | 跨页消息桥 |
275+
| `static/avatar-ui-popup.js` | 开发 C | 首页设置入口与菜单结构 |
276+
| `static/app-interpage.js` | 开发 C | 跨页消息桥 |
277277
| `static/yui-guide-page-handoff.js` | 开发 C | handoff 主体模块 |
278-
| [static/app-ui.js](../../static/app-ui.js) | 开发 C 主导,主负责人收口 | 共管文件 |
279-
| [static/app-buttons.js](../../static/app-buttons.js) | 开发 C 主导,主负责人收口 | 共管文件 |
278+
| `static/app-ui.js` | 开发 C 主导,主负责人收口 | 共管文件 |
279+
| `static/app-buttons.js` | 开发 C 主导,主负责人收口 | 共管文件 |
280280

281281
### 3.7 合并顺序与节奏冻结
282282

@@ -334,7 +334,7 @@ type YuiGuideHandoffToken = {
334334
原因:这是共享契约文件,B 和 C 都会碰。
335335
控制方式:主负责人长期持有;B 只主改 `performance`;C 只主改 `anchor / navigation`
336336

337-
2. [static/app-ui.js](../../static/app-ui.js) / [static/app-buttons.js](../../static/app-buttons.js)
337+
2. `static/app-ui.js` / `static/app-buttons.js`
338338
原因:这两个文件后续既可能被入口包装用到,也可能被“请她离开”等统一动作用到。
339339
控制方式:开发 C 主导,主负责人只做最小收口,开发 B 不直接改。
340340

0 commit comments

Comments
 (0)