RoleSnapshot 在会话签发时冻结
会话签发时,整个角色状态(corpus URI、prompt 正文、skill prompts、允许的工具、拒绝的 capability、拒绝的语料 glob、waypoints、按 capability 的配置、角色 id/name)会被快照进 Redis 里的 session_data;此后该会话再也不会读取角色行(backend/internal/access/entity/role_snapshot.go)。
由此带来的结果:owner 修改一个角色 / prompt / skill,不会影响已经在跑的会话——不存在"正在进行中的会话突然失去访问权限"这种情况。会话中途唯一的补救手段是吊销 access code——唯一的例外是全局 capability 层,它是实时生效的,能立刻杀死会话(confusables)。deniedCapabilities 是显式的(不是做减法),所以它甚至能拦住 ACL=always 的 capability(retrieval、ask_visitor)。
类视图
classDiagram
class RoleSnapshot {
<<frozen at issue - Redis session_data>>
-capConfig map~string,json.RawMessage~ - per-capability config
-frozenAt time.Time
-roleID, roleName string
-promptBody string
-codePromptBody string
-providerID string
-corpusURIs []string - granted globs
-skillPrompts []string
-allowedTools []string
-deniedCapabilities []string - explicit
-deniedCorpusURIs []string - code corpus narrowing
-skillIDs, mcpServerIDs []string
-dockButtons []DockButtonConfig
-waypoints []Waypoint
-requireGhostEvidence bool
-gasMetered bool
+AllowsCorpus(uri, published) bool
+CorpusScope() CorpusScope
+AllowsCapability(capID, aclAlways) bool
+IsZero() bool
+MarshalJSON() / UnmarshalJSON()
}
class RoleSnapshotInit {
exported mirror of every field
the ONLY way in - NewRoleSnapshot(init)
}
class roleRow {
<<postgres - live owner-editable>>
roles + role_skills + role_mcp_servers + role_corpus_uris
}
class capabilitySettings {
<<postgres - the ONE live layer>>
owner_id, capability_id PK
enabled bool
updated_at
}
RoleSnapshotInit --> RoleSnapshot : NewRoleSnapshot
roleRow --> RoleSnapshotInit : copied ONCE at issue
capabilitySettings ..> RoleSnapshot : bypasses the freeze (live AND-term)
全部 18 个字段都是未导出的——快照在构造完成后不可变(RoleSnapshotInit 是唯一的入口,且它通过 Redis 做 JSON 往返)。注意它同时也冻结了 codePromptBody(按 code 区分的 prompt)、该角色的 dockButtons、该 code 的语料拒绝(deniedCorpusURIs,来自 code_corpus_denials)以及每个 capability 的按角色配置(capConfig,对本 domain 不透明)。
AllowsCorpus(uri, published) 会先硬性拒绝 raw://**,再要求命中正向 glob 列表且不命中该 code 的拒绝 glob——同一份被冻结的作用域(CorpusScope())会作为一个不透明的整块,通过 _meta 转发给外部化的 retrieval 插件。