- index.ts: RESERVED_ERROR_MEMBERS JSDoc now says `__x__` (non-empty middle), matching DUNDER_MEMBER and the derivative docs; and PORTABLE_RESERVED_WORDS is described as covering portable *target* languages (Python is a later-PR backend, not yet shipped). - types.ts + type-equiv docs: CodeBindingNamespace.global points to RESERVED_BINDING_GLOBALS by name with an `e.g.` sample rather than enumerating the set (single home), no longer implying `__debug__` is a seeded slot. - Agent Note: separate `__debug__` (CPython compile-time constant) from the seeded bootstrap globals. - README (en/zh): document the four exported exclusion sets and the portable identifier/naming contract — no cross-package link needed. - Regenerate cordis services catalog for the shifted source line.
5.6 KiB
Agent Note:code-runtime seam 拥有可移植标识符排除集
Status: implemented
English | 中文
Problem
code-runtime seam 承诺:在一个后端上有效的绑定命名空间列表,在每个后端上都有效,因此 Code Mode 消费方可以把同一组绑定交给任何已注册的运行时,而不必知道它的语言。首个后端 dsh-code-runtime-worker 私自拥有了执行这项承诺一部分的标识符规则:一个允许 JS 专有 $ 的 IDENTIFIER 正则、一个只含 ECMAScript 关键字的 RESERVED_WORDS 集合,以及一个含三个 JS Error 槽位的 RESERVED_ERROR_PROPERTIES 集合。这些规则描述的是 worker 自身的语言,而非 seam 的可移植性契约。
一个针对不同语言(CPython,将在本 stack 后续 PR 中到来)编写的第二后端,要么重新声明自己的规则——让 lambda 通过 worker 却在 Python 上失败,或让 $tools 通过 worker 却在每个非 JS 后端上失败——要么导入 worker 的规则,从而反转依赖,使 seam 的一个实现伸手进入另一个兄弟实现。二者都无法让可移植承诺成真:它只对调用方恰好测试过的那个后端成立。
Decision
seam 包(@deepseek-ai/dsh-code-runtime)以四个具名常量导出可移植标识符排除契约,每个后端导入它们而非重新声明:
PORTABLE_RESERVED_WORDS——ECMAScript 与 Python 保留字的联集。任何命名空间 global 或 error-class 名称匹配其中之一,都在所有后端上被拒绝,因此lambda即便是合法的 JS 参数名也被拒绝。新增一门语言即扩宽此联集,这是对现有绑定名称的一次有意的破坏性复审。RESERVED_BINDING_GLOBALS——某个后端在程序命名空间中拥有的 global:console(worker 的日志捕获)、__dsh_main__/__builtins__/__name__(Python bootstrap 的包装器与预置模块 global),以及__debug__(不是 seed 的槽位,而是 CPython 编译期常量,赋值会被拒,故以该名注入的 global 不可达——同一种可移植性分裂,只是机制不同)。在所有后端上被拒绝,使命名空间列表无法选到一个在某后端能用、在另一后端冲突的名称。RESERVED_ERROR_MEMBERS——每个后端都拒绝的 error-member 名称:JSError槽位(name、message、stack)与 Python 异常协议成员(args、with_traceback、add_note)。DUNDER_MEMBER——dunder 形式正则(__x__,非空中缀),作为 error member 被整体拒绝,因为其中若干是受约束的 CPython 描述符,其确切集合是解释器版本细节。
seam 同时把可移植标识符子集收窄为 [A-Za-z_][A-Za-z0-9_]*(记录在 CodeBindingNamespace.global 与 CodeBindingErrorClass 上),去掉 JS 专有的 $。worker 直接以 seam 名消费这些共享常量——binding-global 与 error-class 名称用 PORTABLE_RESERVED_WORDS、后端拥有槽位用 RESERVED_BINDING_GLOBALS、error member 用 RESERVED_ERROR_MEMBERS 加 DUNDER_MEMBER——不再本地起别名;其 IDENTIFIER 正则去掉 $。
尽管本 PR 只交付一个后端,这些常量仍置于 seam:要点正是该契约与语言无关,且拥有权在任何单一语言之上。违反它的后端才是 bug,而共享集合正是复审者查看"可移植"含义的地方。
Scope
本 PR 只交付 seam 扩展与 worker 对它的采用。这里不交付任何 Python 后端、py-types 渲染器或 Code Mode 的语言分发——它们是本 stack 中依赖这些导出的后续 PR。seam README 中仅描述 worker 的措辞保持不变,理由相同:链接到一个尚不存在的 dsh-code-runtime-python README 会破坏死链 gate。
RESERVED_BINDING_GLOBALS 当前编码了尚未合并的 Python bootstrap 的具体设计:它恰好 seed __builtins__/__name__,并把程序包装在 __dsh_main__ 之下。任何 seed 额外模块 global(__doc__、__loader__、__spec__、__file__、__package__ 等)的 Python 后端 PR 必须在同一改动中扩宽此集合,正如新增一门语言即扩宽 PORTABLE_RESERVED_WORDS——bootstrap 会 seed 却不在集合中的名称,正是本契约要防止的可移植性分裂。
Alternatives considered
每个后端声明自己的排除集。 拒绝:这让可移植承诺变成逐后端成立。调用方在 worker 上测过的绑定列表可能被 Python 拒绝,而这正是 seam 存在要防止的分裂。
Python 后端导入 worker 的常量。 拒绝:这反转依赖——seam 的实现会为一个二者都不拥有的契约伸手进入兄弟实现。契约属于二者之上,即 seam。
在可移植标识符子集中保留 $。 拒绝:$ 是 JS 专有拼写。允许它会让 $tools 通过 worker 却在每个非 JS 后端上失败,为纯粹表面的好处破坏可移植性。
Consequences
获得:一个地方——seam 包——定义什么是可移植绑定名称,每个后端通过导入执行同一契约。在一个后端上有效的命名空间列表在所有后端上都有效,这是可验证的,而非取决于调用方测试了哪个后端的巧合。
代价:现有使用含 $ global 的 worker 调用方现在会在标识符校验时失败。在预发布立场下这是一次被纠正的地基,而非需要 shim 的兼容性破坏。worker 的 seam-misuse 测试新增了 $tools、Python 异常成员(args)、dunder(__dict__)与一个 Python 拥有的 global(__dsh_main__)等用例,从 worker 侧证明共享集合被执行。