Files
Cyrene_v2/internal/capability/paneldecl/when.go
T
AskaEth c9144c1096
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled
feat(plugins): 插件「管理面板声明」库——声明模型 + 闭集校验 + catalog(design/09 §5.2/§5.2.1)
纯库、零接线:不碰 gateway/frontend/Docs/.env*(本片刻意不接线,避免与在途切片争用文件面)。

- pkg/plugins/sdk:面板声明类型(JSON tag 与 design/09 §5.2 示例逐字同形);
  字段类型与 section 类型都是**闭集**;When 三态 + **严格 UnmarshalJSON**(未知条件键报错,
  不静默丢)
- internal/capability/paneldecl:闭集校验(47 条拒绝用例,断言的是**分类哨兵 + JSON 路径**,
  不是错误字符串)· catalog(并发安全、返回拷贝、按 (group, order, 登记序号) 的**全序**稳定排序)·
  when 求值(上下文由 Resolver 注入,本包不读环境变量/文件)· 角色过滤(viewer 只留
  status/log/table 且摘掉 rowActions,纯写面板整体隐藏;未知角色 fail-closed)
- 拒绝粒度:插件 id 非法 ⇒ 整插件拒绝;单个面板非法 ⇒ 只拒该面板 + 聚合显式报错(理由见报告)

【我的验收】隔离 worktree(19 文件)build/vet/linux/darwin 全过、69 包全绿;
我亲手打红变异:把同组同序的回退比较  反成  ⇒ TestCatalog_StableOrder FAIL。
(记录一条观察:比较器已靠 seq 构成全序,故 sort.SliceStable 的稳定性是冗余保护 ——
我第一次用「SliceStable→Slice」打不红,正是因为全序与算法无关;这不是测试虚护。)
2026-10-06 08:06:20 +08:00

41 lines
2.1 KiB
Go

package paneldecl
import "git.yeij.top/AskaEth/Cyrene_v2/pkg/plugins/sdk"
// 本文件是 `when` 条件显示的求值(§5.2.1 nav.item:「`when` 不满足 ⇒ 条目不出现」)。
// Context 是 when 求值的上下文:**调用方注入的最小事实**。
//
// 为什么本包不自己去问「这个插件启用了吗」:AGENTS §9 要求单一事实来源,启用状态的真源在
// 插件管理器(§5.2.1 B 的生命周期一栏),本包去读 config / env / 文件只会造出第二份可能
// 不一致的状态,而且让单测依赖外部环境。所以这里只声明「求值需要什么」,由调用方填。
type Context struct {
// Enabled 是「该插件当前已启用」这一事实。install / enable / disable / uninstall
// 之后由管理器给出最新值,对应 §5.2.1「都重算面板集合」。
Enabled bool
}
// Resolver 按插件 id 给出求值上下文。用函数类型而不是接口:调用方通常只是读一张 map 或
// 问一次管理器,闭包足够,不值得为它定义一个类型。
//
// 约束(写在这里因为编译器管不了):Resolver 不得回调 Catalog 的 Register/List/Groups ——
// 那些方法持有目录锁,回调会自锁死。
type Resolver func(pluginID string) Context
// Eval 求值一份 when 声明,报告该面板的入口是否应当出现。
//
// 语义(本期闭集只有 enabled 一个条件键):
// - when 为 nil、或对象里没写 enabled ⇒ **无条件** ⇒ 显示(条件显示是可选能力,
// 不写就等于不设条件);
// - when.enabled = true ⇒ 仅当 ctx.Enabled 为真时显示(§5.6.1「未启用的插件 ⇒ 入口不出现」);
// - when.enabled = false ⇒ 仅当 ctx.Enabled 为假时显示 —— 条件是「要求值相等」,
// 写 false 不等于没写(把 false 当「无条件」会让声明表达力凭空消失一半)。
//
// 「不满足」的效果是**入口整个不出现**,不是灰掉(§5.2.1 nav.item 明写)。
func Eval(w *sdk.When, ctx Context) bool {
if w == nil || w.Enabled == nil {
return true
}
return ctx.Enabled == *w.Enabled
}