问题
allowed_tool_names 用一个 None 同时表达两种语义完全不同的状态:
# src/loushang/harness/capabilities/tools.py:134
allowed_names: Iterable[str] | None = None,
# :141-144
self._allowed_names = (
frozenset(_unique_names(allowed_names)) if allowed_names is not None else None
)
# :314
def is_allowed(self, name: str) -> bool:
return self._allowed_names is None or name in self._allowed_names # ← None 恒为 True
实测三种状态(ToolActivationCoordinator,available=read/bash/edit):
allowed_names |
is_allowed("read") |
is_allowed("bash") |
active |
None |
True |
True |
('read','bash','edit') |
[] |
False |
False |
() |
["read"] |
True |
False |
('read',) |
None = 无上限(fail-open),[] = 无工具,两者语义相反但都是"缺省/空"的直觉候选。
为什么这不是风格问题
None 是 AgentProductSession.__init__ 的参数缺省值(harness/session/agent_product.py:370):
allowed_tool_names: list[str] | None = None,
省略该参数 = 无上限。 调用方不会报错,只会静默 fail-open。这与本仓库自身的既定原则冲突:
tool-governance.md:392:Model Call preparation 超时/失败时返回 default_selection_pending 并 fails preparation closed;
capability-runtime-convergence-pr0-baseline.md:94(COMP-03):ProductRuntimeBindings.register_tool 的默认绑定构造兼容,但试图实际注册工具时 fail closed。
即:仓库在别处明确选择 fail-closed,此处却 fail-open,且缺省值静默生效。
服务端场景下的实际影响
多用户/多租户托管中,"装配时忘记传工具上限"是一个现实的编码错误。后果是该会话获得工具目录里的全部工具,且没有任何报错或诊断。这与 allowed_tool_names 在服务端承担的角色(唯一能在装配期收窄工具面的手段)直接矛盾。
需要注意的是:这不是唯一防线,也不是"越权"——Invocation Policy 仍然逐次求值。但它确实把"装配期上限"这一层静默移除了。
目标态已经解决了一部分
tool-governance.md 的 governed-v1 模型已经区分了这两种状态,没有 None 歧义:
IntentSelectionMode.INHERIT_DEFAULTS # 默认继承 + 显式增删
IntentSelectionMode.EXPLICIT_ONLY # 只有显式启用项,可为空
set_explicit_only(names, expected_revision) 明确支持"空选择"(tool-governance.md:655 "including an empty no-tools selection")。
所以本 issue 的性质是:legacy 路径的缺省值与目标态语义不一致,而 P1B cutover 尚未完成(tool-governance.md Status: "existing Product sessions remain on isolated legacy_positive state pending the P1B atomic control-surface cutover",tracking #517)。
建议
A. 若纳入 P1B(推荐):cutover 时对 None 采用 fail-closed 缺省,并显式提供"无上限"入口(如 IntentSelectionMode 或 ToolSelection.ALL 具名值)。迁移期可先加构造期诊断。
B. 若需在 P1B 前独立修复:把 None 拆成可区分的三态:
ToolSelection = Literal["all"] | frozenset[str] # 或专用类型
# 缺省改为 frozenset()(无工具),"all" 必须显式书写
迁移注意:全仓 allowed_tool_names=None 的调用点较多(含测试),且 Coding CLI 依赖 None 表示"不限制"(coding/cli/__main__.py:232-238)。需同步审计所有调用点,并在 AgentProductSession / SessionToolRuntime / bootstrap_construction 三处保持一致。
验收要点
相关
问题
allowed_tool_names用一个None同时表达两种语义完全不同的状态:实测三种状态(
ToolActivationCoordinator,available=read/bash/edit):allowed_namesis_allowed("read")is_allowed("bash")None('read','bash','edit')[]()["read"]('read',)None= 无上限(fail-open),[]= 无工具,两者语义相反但都是"缺省/空"的直觉候选。为什么这不是风格问题
None是AgentProductSession.__init__的参数缺省值(harness/session/agent_product.py:370):省略该参数 = 无上限。 调用方不会报错,只会静默 fail-open。这与本仓库自身的既定原则冲突:
tool-governance.md:392:Model Call preparation 超时/失败时返回default_selection_pending并 fails preparation closed;capability-runtime-convergence-pr0-baseline.md:94(COMP-03):ProductRuntimeBindings.register_tool的默认绑定构造兼容,但试图实际注册工具时 fail closed。即:仓库在别处明确选择 fail-closed,此处却 fail-open,且缺省值静默生效。
服务端场景下的实际影响
多用户/多租户托管中,"装配时忘记传工具上限"是一个现实的编码错误。后果是该会话获得工具目录里的全部工具,且没有任何报错或诊断。这与
allowed_tool_names在服务端承担的角色(唯一能在装配期收窄工具面的手段)直接矛盾。需要注意的是:这不是唯一防线,也不是"越权"——Invocation Policy 仍然逐次求值。但它确实把"装配期上限"这一层静默移除了。
目标态已经解决了一部分
tool-governance.md的 governed-v1 模型已经区分了这两种状态,没有None歧义:set_explicit_only(names, expected_revision)明确支持"空选择"(tool-governance.md:655"including an empty no-tools selection")。所以本 issue 的性质是:legacy 路径的缺省值与目标态语义不一致,而 P1B cutover 尚未完成(
tool-governance.mdStatus: "existing Product sessions remain on isolatedlegacy_positivestate pending the P1B atomic control-surface cutover",tracking #517)。建议
A. 若纳入 P1B(推荐):cutover 时对
None采用 fail-closed 缺省,并显式提供"无上限"入口(如IntentSelectionMode或ToolSelection.ALL具名值)。迁移期可先加构造期诊断。B. 若需在 P1B 前独立修复:把
None拆成可区分的三态:迁移注意:全仓
allowed_tool_names=None的调用点较多(含测试),且 Coding CLI 依赖None表示"不限制"(coding/cli/__main__.py:232-238)。需同步审计所有调用点,并在AgentProductSession/SessionToolRuntime/bootstrap_construction三处保持一致。验收要点
None不再静默等价于"无上限"allowed_tool_names=None调用点逐一审计并分类(真"无上限" vs 遗漏)agent_product/tool_runtime/bootstrap_construction)语义一致相关
docs/internals/architecture/drafts/server-side-harness-and-business-product.md§4.4、§7.3)