mirror of
https://gitcode.com/JianFeeeee/HomeAgent.git
synced 2026-11-06 01:07:35 +00:00
fix(plugin): 修两处持锁自死锁(CI 双红真因)+ tools_ready 事件声明
## 死锁(这是 CI 连续两次红的真因) `StopAll` 与 `StopPlugin` 都在持有 `r.mu` **写锁**时调用 `RemoveBuiltinSkill` → `skillDirOf` → `r.mu.RLock()` ⇒ **自死锁**。 后果不是报错而是**卡死**: - internal/plugins 卡 300s 超时(TestCapability_RestrictedPluginStillLoads) - internal/plugin 卡 840s 超时(TestStopPluginDoesNotTouchPersistentState) - CI 的 `go test ./...` 与 `go test -race` 双双红 修法:拆出**不加锁**的内部版本,持锁路径只走它: - `skillDirFrom(dataDir, name)`(纯函数) - `removeBuiltinSkillLocked(pluginName, p, dataDir)`(显式传 dataDir, 避免"看起来没取锁、一改就取锁"的隐患) - `announceSkillRemovedLocked`(持锁下直读 r.evBus) 修后:internal/plugins 300s → 12s,internal/plugin 840s → 0.7s。 ## 防回归判据 `TestStopAllAndStopPluginDoNotDeadlock`:死锁**不返回**,普通断言抓不到, 所以用**超时**当判据(5 秒未返回即失败),并指明应改用 Locked 版本。 ## tools_ready 事件声明 新增的 tools_ready 触发 `TestEveryPublishedEventHasAnOutwardPath` (该判据防"事件发了、客户端收不到"的静默失效)。 tools_ready 是**内核内部**就绪信号(plugin-host → seq 等订阅者), 不是对话流程事件,前端无需感知 ⇒ 在 allowed 里显式声明理由。 ## 验证 go build ./... / go vet ./... / go test -p 1 ./... / -race 全通过。
This commit is contained in:
@ -259,10 +259,34 @@ func (p *Plugin) SkillIndex() string {
|
||||
if desc == "" || desc == "---" {
|
||||
desc = "(无描述)"
|
||||
}
|
||||
if len(desc) > 80 {
|
||||
desc = desc[:80] + "..."
|
||||
}
|
||||
// ★ 截断必须按**字符**(rune)而不是字节。
|
||||
//
|
||||
// 原实现 `desc[:80]` 按字节切,而中文占 3 字节 ⇒ 切点常落在
|
||||
// 多字节字符中间,产生**非法 UTF-8**。后果实测:
|
||||
//
|
||||
// seq-tools v - 把反复出现的同类工具操作编成「序列」存起来,以后一次<E4B880>
|
||||
//
|
||||
// 模型看到的是乱码尾部,且"什么时候该用"那部分整段丢失 ——
|
||||
// 于是它根本不知道这个 skill 何时适用(e2e 实测:该用 seq 的
|
||||
// 多对象重复场景,模型改去调 file-tools)。
|
||||
//
|
||||
// 本仓 25 个 skill 里有 8 个(32%)踩过这个截断。
|
||||
// 判据见 TestSkillIndexTruncatesByRune。
|
||||
desc = truncateDesc(desc)
|
||||
fmt.Fprintf(&b, "%s v%s - %s\n", e.sk.Name(), e.sk.Version(), desc)
|
||||
}
|
||||
return strings.TrimRight(b.String(), "\n")
|
||||
}
|
||||
|
||||
// truncateDesc 把 skill 描述截断到 80 个**字符**(rune),超出加省略号。
|
||||
//
|
||||
// 必须是字符而不是字节:中文占 3 字节,按字节切会落在多字节字符中间,
|
||||
// 产生非法 UTF-8。见 TestSkillIndexTruncatesByRune 的真实缺陷说明。
|
||||
func truncateDesc(desc string) string {
|
||||
const maxRunes = 80
|
||||
r := []rune(desc)
|
||||
if len(r) <= maxRunes {
|
||||
return desc
|
||||
}
|
||||
return string(r[:maxRunes]) + "..."
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user