---
title: "Calcit API 命名角色"
summary: "区分 API 角色；按家族给出命名决策、类型与失败契约、等价迁移和暂缓边界"
scope: "core"
kind: "guide"
category: "features"
aliases:
  - "API naming"
  - "constructor naming"
  - "method naming"
  - "API vocabulary"
  - "命名决策"
entry_for:
  - "calcit query type"
  - "calcit query def"
id: core/features/api-roles
parent: core/features
related:
  - core/features/type-guidance
  - core/run/query
  - core/agent
leads_to:
  - core/run/query
---

# Calcit API 命名角色

调用前先判断角色，再找拼写：类型名描述值的形状，构造器创建值，方法以已知类型的值为接收者，命名空间函数执行独立操作，内部函数只是编译器或 core 的实现入口。同一操作在内部可能有多种表示，不代表每种表示都应成为用户的首选 API。

这套规则参考 Rust API 中「类型、构造、方法各有位置」的可预测性，但不照搬 snake_case 或 `Type::method` 语法。Calcit 保留有意义的 kebab-case、谓词 `?` 和作用标记 `!`。具名 Enum 定义同时是 schema 中的类型名和构造值时的可调用入口：`Option :some value` 与 `Result :err error` 中的类型名都必须解析到对应 core 定义。`%some/%none/%ok/%err` 是旧构造 helper，已有 [#1420](https://github.com/calcit-lang/calcit/issues/1420) 的 fix 可迁移；本页只推荐直接构造。

## 表层命名矩阵

`read-file/read-dir` 的公开名称与底层 primitive 分离：底层分别使用 `&read-file/&read-dir`，公开调用仍保留原有参数、返回值和抛错行为，JS runtime 与 host injection 名称不变。模块可以声明同名定义并通过正常命名空间引用（例如 `fs/read-file`）复用；局部遮蔽 core 名称仍接受既有严格诊断，不因此放宽。新代码优先采用 typed `FsPath` 方法；这项 reader 修复不让旧读取接口获得新的 WASM 能力。

| 角色 | 首选形态 | 命名与类型依据 | 不作为新代码入口 |
| --- | --- | --- | --- |
| 类型、trait、Enum 变体 | `Person`、`Option`、`Result`；`:some`、`:err` | 类型名用 PascalCase；variant 用 tag，由定义验证 payload；`Option<T>` / `Result<T,E>` 写进 schema | 把 `%some` 当作类型或把裸 `Option` 当作已构造的值 |
| 构造 | `Person :name name`、`Option :some value`、`Result :err error`；集合字面量 `[]` / `{}` | 调用已知名义定义，保留 `T -> Option<T>` 等具体关系，不以 `%` 前缀猜构造类别 | `%{}`、`%::` 的静态可替代场景；已删除的 `%some/%none/%ok/%err` |
| 接收者方法 | `value .unwrap`、`text .includes? fragment`、`items .get index` | 接收者类型决定可用方法与返回类型；名称用 kebab-case，布尔谓词以 `?` 结尾；`query type` 应显示 `proven` | 在业务代码直接使用 `option:unwrap`、`&str:includes?` |
| 无接收者函数 | `parse-float source`、导入的 `module/function` | 行为无自然接收者，或显式跨模块复用；名称用 kebab-case；`parse-float: String -> Result<Number,String>` | 为已有 typed receiver 另造平行的公开 `type:verb` 函数 |
| 内部 helper 与 primitive | `option:unwrap`、`&str:includes?`、`&list:count` | `:internal` 标签与实现链接表明它们是 core/lowering 细节；`&` 保留给 runtime/backend primitive | 把内部路径写成应用文档中的首选 API，或仅凭名字推断其类型安全性 |

`?` 表示返回布尔判断，`!` 只用于确有作用或特殊控制语义的公开名字；不要机械地给每个动词添加后缀。模块名和普通函数继续用 kebab-case。公开名称是否是方法由类型契约和解析证据决定，不由字符串里是否有 `:` 或 `&` 决定。内部 helper 与 primitive 暂有不同实现命名；它们不是两套公开语言风格，不承诺用户可依赖其拼写。下一步先迁移公开调用，再根据真实编译器重复逻辑决定是否调整内部名字，避免为了表面一致重做 lowering。

已知 `List<T>` 的 `.first`、`.last`、`.get` 和 `String` 的 `.first`、`.last`、`.nth` 使用同一接收者证明推导 `Option<T>` 或 `Option<String>`；`calcit query type "quote $ List Number" --format edn` 可查看参数、返回值和 `proven` 状态。List 的旧 `.nth` 已在 0.29.0 删除，改用 `.get`。开放接收者或 Enum 的异质 payload 不能仅凭方法名得到具体成员类型；查询保持开放证据，不把 `Dynamic` 伪装为已证明的 `T`。

同类型的 `Option` / `Result` 值可以直接用 `=` 比较，名义 Struct 字段读取与直接构造值采用同一契约。空变体没有 payload 时，可先绑定预期值并用 `assert-type` 补充完整泛型类型，再与字段比较；payload 类型仍必须匹配。判断是否有值也可使用 `.some?` / `.none?`，不要把 Option 与 nil 或裸 payload 比较。

### English reference for Agents

| Role | Preferred public form | Legacy or implementation form |
| --- | --- | --- |
| Type and variant | `Option<T>` with `:some` / `:none`; `Result<T,E>` with `:ok` / `:err` | A bare type name is not a constructed value. |
| Constructor | `Option :some value`, `Result :err error`, `Person :name name` | `%some`, `%none`, `%ok` and `%err` were removed in 0.31.0; write `Option :some value`, `Option :none`, `Result :ok value` and `Result :err error`. |
| Receiver method | `value .unwrap`, `text .includes? fragment` | `option:unwrap value` and `&str:includes? text fragment` are implementation paths, not preferred application calls. Preview proven Option rewrites with `calcit fix --rule core-option-method-v1 --format edn`. |
| Module function | `parse-float source`, imported `module/function` | Use when there is no natural typed receiver; do not introduce a parallel public `type:verb` spelling. |
| Internal helper / primitive | No public spelling promised | `option:unwrap` is a core helper; `&str:includes?` and `&list:count` are runtime primitives. Trace them for implementation, not API discovery. |

The public, human-facing form is distinct from macro-expanded core definitions and JS/native/WASI lowering names. Type schemas and receiver resolution—not punctuation alone—establish a method's contract. Keep Calcit's kebab-case, `?` predicates, and meaningful `!` effects; Rust is a model for clear roles, not a mandate to copy Rust syntax. Deprecated constructors may remain during migration, but Agent-facing examples should show one preferred spelling. Ambiguous or shadowed calls require manual review rather than an unproven rewrite.

`Option` / `Result` 等名义定义即使在 core 元数据中带 `:internal`，也可以是公开的 schema 类型和直接构造入口；判断是否推荐给应用要看其**角色和调用契约**，不能只按一个 tag 或定义路径过滤。

| 遇到的写法 | 新代码首选 | 迁移与限制 |
| --- | --- | --- |
| `%ok value`、`%err error`、`%some value`、`%none`（均已在 0.31.0 删除） | `Result :ok value`、`Result :err error`、`Option :some value`、`Option :none` | 使用 0.30.x 的 CLI 运行 `core-nominal-constructor-v1`；被遮蔽或当函数值传递时人工确认 |
| `option:unwrap value`、`option:unwrap-or value fallback` | `value .unwrap`、`value .unwrap-or fallback` | `core-option-method-v1` 只自动改写接收者有编译器证明的调用；core 空 Option 可由具体 fallback 补足类型，其他开放类型、未知 macro、函数值引用仍需人工确认 |
| `result:unwrap-or result fallback` | `result .unwrap-or fallback` | `core-result-method-v1` 只改写目标方法已证明的调用；core `:err` 可由具体 fallback 确定成功类型，同时保留错误类型；开放 Result、`:ok` 动态值和遮蔽调用仍需审阅 |
| `option:some?` / `option:none?`、`result:ok?` / `result:err?` | `value .some?` / `.none?`、`value .ok?` / `.err?` | 沿用对应 Option / Result 方法规则，仍须证明接收者名义类型及同一 core 方法；裸 Dynamic 不能借 Bool 返回值绕过类型检查 |
| `&str:includes? text fragment`（容易被误记为 `&string:include?`） | `text .includes? fragment` | 当前内部定义的准确拼写是 `&str:includes?`；String 的旧 `.contains?` 检查索引，已在 0.29.0 删除，索引查询使用 `.contains-index?`，不能把索引与子串查询机械互换 |
| 静态 `%:: Enum :variant payload`、`%{} Struct (:field value)` | `Enum :variant payload`、`Struct :field value` | 只有类型定义能静态解析、variant/字段契约可证明时才自动修复；动态 prototype 边界仍可显式使用低层形式 |

这张对照表只规定**公开源码的首选写法**，不是要求一次删除所有内部实现名。内部函数即使仍出现在 `query type` 的定义路径中，也不得反向成为 Agent 推荐的应用代码。

### 索引单位：普通索引与显式 byte 单位

String 的 `.len`（旧 `.count` 方法已在 0.29.0 删除）、`.get/.nth/.slice` 与 `.find-index` 使用 Unicode 标量单位；查找返回 `Option<Number>`，不是 Rust `str::find` 的字节偏移。Calcit 借鉴语义清晰的命名，不照搬会破坏自身索引一致性的底层表示。`.includes?` 判断子串，`.contains-index?` 判断索引存在（List 与 String 的旧 `.contains?` 已在 0.29.0 删除），不因统一索引单位而互换。协议长度使用显式 `&str:utf8-byte-count`，不可混入普通索引。示例和边界见 [String](../data/string.md#子串搜索索引)；其他 API 族的重命名仍由 #1452 逐项决策，本修复不增加别名或 fix 规则。

## 逐族命名决策（0.27–0.28）

这是 [#1452](https://github.com/calcit-lang/calcit/issues/1452) 的实施契约，不是“以下新名字已经可用”的 API 清单。**保留**表示当前首选不变；**目标**表示本轮确定的新拼写、仍待对应 issue 实现；**内部**表示只保留可追溯的实现角色；**暂缓**表示已有语义或类型问题尚不能安全定名。Agent 生成代码应先查当前版本的 `query type/def`，不得调用尚未实现的目标。

表中的签名包含 receiver；`T/U/K/V` 是保留关系的类型参数，不是 Dynamic。只有旧新入口的来源、参数/返回类型、失败行为和求值顺序均能证明等价，才允许自动迁移。这里的迁移分类不增加 CLI flag、命名 linter 或第二份公开 API registry。

### 词法与职责

- 类型/trait 用 PascalCase；值、函数、方法、模块、字段用 kebab-case；variant 继续使用 `:some/:err`。保留 `[]/{}/#{}`、算术/比较运算符、`defn/let/->/->>` 等结构化 Lisp 表达，不把宏改成 Rust 的 `name!` 语法。
- 返回 Bool 的判断保留 `?`，优先 `empty?`、`contains-key?`，不额外套 `is-…?`。`Option<Number>` 查找结果不是谓词；名字不能代替类型证明。
- 实例操作优先 typed `.method`；没有自然 receiver 时保留普通模块函数 `alias/name`，不再并列增加公开 `type:verb` 别名。
- 参考 [Rust 命名指南](https://rust-lang.github.io/api-guidelines/naming.html) 的角色划分和语义词汇，不照搬 snake_case、借用/所有权对应的 `as_/into_` 或惰性 iterator。Calcit 集合操作返回新值；普通 String 索引是 Unicode 标量，不是 Rust 字节偏移。
- 本轮目标是让入口与语义更可预测；不宣称仅改名就能证明 LLM 正确率提升。验收使用实际查询、迁移和消费者运行，不引入新的评分系统。

### 谓词与成员查询

实施任务：[#1454](https://github.com/calcit-lang/calcit/issues/1454)。成员判断选用现有 `.includes?`；**不把旧 `.contains?` 原地改成另一种含义**，也不安排先移走旧义、再复用同名的二次迁移。

| 当前入口与签名/行为 | 决策与目标签名 | 迁移边界 |
| --- | --- | --- |
| `some?: T -> Bool`，仅排除 nil；Option none 也为 true | **已提供** `non-nil?: T -> Bool`；`some?` 已在 0.29.0 删除 | 等价改名，不改成 Option `.some?`；WASM 已以类型证据修复 false/0 误判；迁移规则 `core-non-nil-predicate-v1` 仅 0.28.x 的 CLI 提供 |
| Option `.some?/.none?: Option<T> -> Bool`；Result `.ok?/.err?: Result<T,E> -> Bool` | **保留**，判断名义 variant | 空值、false 和 nil payload 不改变 variant；既有 helper 退场条件仍适用 |
| List/String `.contains?: (receiver, Number) -> Bool`，检查索引 | **已提供** `.contains-index?`，签名与原有错误行为不变；旧 `.contains?` 已在 0.29.0 删除 | List 的负数/小数/非有限索引返回 false；String 的负数/小数报错、越界返回 false。它不是元素或子串查询；旧调用报 `E_RETIRED_METHOD`，自动迁移需用 0.28.x 的 CLI 运行 `core-predicate-method-v1` |
| Map `.contains?: (Map<K,V>, K) -> Bool`；`.includes?: (Map<K,V>, V) -> Bool` | **已提供** `.contains-key?`、`.contains-value?`，分别接收 K/V；旧 Map `.contains?` 与 `.includes?` 已在 0.29.0 删除 | 即使 K 与 V 同型，两种命题也不同；旧调用报 `E_RETIRED_METHOD`，自动迁移需用 0.28.x 的 CLI 运行 `core-predicate-method-v1` |
| Struct `.contains?` 检查字段；Enum `.contains?` 检查 tag 与 payload 位置 | Struct **已提供** `.contains-field? Tag -> Bool` 与 `contains-field? Struct Tag -> Bool`；Enum **已提供** `.contains-index? Number -> Bool` | Struct 新接口仅以 Tag 字段名承诺跨目标一致；旧底层 `&struct:contains?` 在 native/JS 还接受 String/Symbol，但不据此扩大新接口类型。Enum 的 0 是 tag，1 起是 payload；新方法仅接受非负有限整数，旧 `.contains?` 会将范围内小数误判为存在，不可无条件自动迁移。自定义 `Contains` 仍需单独证明 |
| String `.includes?: (String, String) -> Bool`；List/Set `.includes?: (C<T>, T) -> Bool` | **保留** `.includes?`；Set 的同义入口 `.contains?` 已在 0.29.0 删除 | String 是子串，其余为成员；前缀函数 `contains?` / `includes?` 仍接受 Map 与 Set |
| `round?: Number -> Bool`，有限且无小数部分 | **已提供** `integer?/.integer?`；Number `.round?` 已于 0.29.0 删除，函数 `round?` 已于 0.31.0 删除 | 新入口复用已验证语义：NaN/±Infinity false，-0 true，非零小数 false；近零/Infinity 的旧行为变化见 [升级说明](../run/upgrade.md#整数谓词的跨目标语义修复)。Bool 不等于 Int refinement 证明；升级前用 0.30.x 的 `core-integer-predicate-v1` 迁移 reader 解析出的内建 `round?` 调用；开放接收者和用户方法不自动改写 |
| `every?` / `any?`，predicate 返回 Bool，短路；空集分别 true/false | **目标** `all?` / **保留** `any?`，参数顺序和短路不变 | 先补足已有支持的 receiver/callback 类型关系；不凭当前宽 schema 承诺所有容器，或凭改名新增方法 |
| `nil?/empty?/blank?/starts-with?/ends-with?/even?/odd?` | **保留**现有签名和命题 | 空白不等于空串；其他已有明确类型谓词同样不为相似拼写强改 |

`Contains` 不能直接别名成一个新 trait：它曾横跨索引、键、字段与成员。#1454 先为上述命题建立具体签名，逐一迁移 builtin 与已定位的自定义 impl；0.29.0 起 List、String、Map 与 Set 已不实现 `Contains`，只剩 Struct 与 Enum，其旧 bound 和具名 trait-call 在兼容窗口保持原契约。泛型接收者有唯一 `where T: Contains` 来源时，调用应绑定该 trait，而非仅按实例上同名方法查找；具体接收者同时实现多个同名 trait 时，仍需显式 `&trait-call` 消歧。0.28.x 的 `core-predicate-method-v1` 仅覆盖已证明的 builtin receiver 和同一实现，普通用户自定义同名方法及 trait-bound 泛型调用不自动改写；该规则已在 0.29.0 随旧方法退役。不得把泛型 `Contains<T,K>` 草率替换成更宽 Dynamic 或猜测性的 trait 联集。

前置缺陷不能由改名掩盖：WASM 的 false/0 误判已经改为依据静态类型证据 lowering；具体参数及直接调用的泛型 helper 会单态化，无法证明类型的开放导出或一等函数边界则以 `E_WASM_NIL_TYPE_EVIDENCE` 拒绝。native、JS、core WASM 与 WASI Component 共享同一组 Calcit 定义测试；详细迁移边界见[升级说明](../run/upgrade.md#wasm-的-nil-类型证据)。在此基础上，`non-nil?` 已成为首选名字，旧 `some?` 已在 0.29.0 删除；残留调用报告未知名字并提示 `non-nil?`，迁移规则 `core-non-nil-predicate-v1` 仅 0.28.x 的 CLI 提供。`round?` 的近零/无穷差异已由共享测试修复，`integer?/.integer?` 复用该语义；函数及 Number 方法的显式迁移规则见 [fix 文档](../run/fix.md)。List/String 索引和 Map 键/值的新方法已提供，对应的旧 builtin 方法已在 0.29.0 删除；Struct/Enum 与 `Contains` trait 的迁移仍由 [#1482](https://github.com/calcit-lang/calcit/issues/1482) 跟踪。

下面是**当前旧契约的反例**，说明为什么不能只凭词形替换；对应 definition `:tests` 会随等价迁移一起保留这些断言：

```cirru
do
  assert= true $ non-nil? $ Option :none
  assert= false $
    Option :none
    , .some?
  assert= true $
    [] 10 20
    , .contains-index? 1
  assert= false $
    [] 10 20
    , .includes? 1
  assert= true $
    {} $ :key :value
    , .contains-key? :key
  assert= false $
    {} $ :key :value
    , .contains-value? :key
```

### 集合长度、组合与遍历

实施任务：[#1455](https://github.com/calcit-lang/calcit/issues/1455)。参考 [Rust fold/reduce](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.fold) 对“有初始值”和“无初始值”的区分，但不引入 iterator/ownership。

| 当前入口与签名/行为 | 决策与目标签名 | 迁移边界 |
| --- | --- | --- |
| List/Map/Set/String `count/.count` 与 `Len/.len: C -> Number` 重叠 | **保留并首选** `.len`，收敛 Countable 的这些用途；String `.count` 方法已在 0.29.0 删除 | 参数单次求值；String 仍按标量，函数 `count` 与 Countable 约束仍接受 String。用户 Countable impl/bound 单独迁移；Struct 字段数与 Enum payload 数 **暂缓**，不把所有 count 当容器长度；**已退场**：0.31.0 起 List/Map/Set 具体接收者上的 `.count` 报 `E_RETIRED_METHOD` 并给出 `.len`；函数 `count` 与 `Countable` 约束（含泛型 `x .count`）继续可用。迁移需在升级前用 0.30.x 的 CLI 运行 `core-collection-len-v1`。 |
| List `.add/.append: (List<T>, T) -> List<T>`；底层 Add trait 是两 List 组合 | **保留** `.append` 与 `.concat`，退场 List 单元素 `.add` 别名 | 先解决 originless `.add` 遮蔽；旧入口失效后也不能静默暴露另一种参数语义。名义 `Add` 调用/泛型界限保留单独验证；**已退场**：0.31.0 起 List `.add` 报 `E_RETIRED_METHOD` 并给出 `.append`；名义 `Add` 调用与泛型界限保持不变。迁移需在升级前用 0.30.x 的 CLI 运行 `core-list-add-v1`。 |
| Map `.assoc: (Map<K,V>, K,V) -> Map<K,V>` / `.dissoc: (Map<K,V>, K, & K) -> Map<K,V>`；Set `include/exclude` 返回新 Set | **本阶段首选**现有持久集合入口；不新增 Map/Set `.insert/.remove`，见 [#1479](https://github.com/calcit-lang/calcit/issues/1479) | Rust 同名方法可能暗示原地修改与不同返回值。Set `.add` 与 Map `.add` 已在 0.29.0 删除：Set 改用 `.include`；Map `.add` 只验证二元 List entry 的形状，不能证明 key/value 为 K/V，需人工改成 `.assoc key value` |
| `foldl/.foldl/foldl'/reduce/.reduce: (List<T>, U, (U,T)->U) -> U` | **已引入** seeded `.fold`；`foldl'` 与 List `.reduce` 已在 0.29.0 删除，前缀 `reduce` 与 `.foldl` 的退场留给 [#1458](https://github.com/calcit-lang/calcit/issues/1458) | 保持左到右、空 List 返回初值、异类型 accumulator、callback 次数；不把旧 reduce 换成无初值语义。新方法证明不弱于现有 `.reduce` |
| `join/.join: (List<T>, T) -> List<T>` | **已引入** `intersperse/.intersperse`；`.join` 方法已在 0.29.0 删除，前缀 `join` 已在 0.31.0 删除 | 只插入同类型分隔项，保持空/单项/重复值/顺序；不改成 String 返回，也不与 `join-string` 混用 |
| `join-str/.join-str: (List<T>, String) -> String`，逐项格式化 | **已引入** `join-string/.join-string`；List `.join-str` 与前缀 `join-str` 已在 0.29.0 删除 | 保留 List<T>、原有显示规则和空 List 结果；不与返回 List 的 `intersperse` 混淆 |
| `vals` / Map `.values: Map<K,V> -> Set<V>`，去重 | **已引入** `distinct-values/.distinct-values`；`.values` 方法已在 0.29.0 删除，前缀 `vals` 已在 0.31.0 删除 | 新旧都返回去重 Set，顺序不保证；保留重复值的视图是独立语义任务，本轮不复用旧名 |
| `foldl-shortcut xs init default f`，reducer 返回 `:: Bool acc` | **已引入** `fold-while xs init f` 与 `ControlFlow`：reducer 返回 `ControlFlow :continue acc` 或 `ControlFlow :break value`，从未 break 时返回累积值；旧入口用 Bool 元组表示停止，并在从未停止时返回额外的 `default` | 前缀 `foldl-shortcut` 已在 0.31.0 删除；语义不同，没有自动 fix，按 `upgrade.md` 的对照人工改写 |
| `section-by xs n: (List<T>, Number) -> List<List<T>>`，按固定长度切段 | **已引入** `chunks`，语义与 Rust `chunks(n)` 一致：最后一段可以更短，`n` 小于 1 时报错；`-by` 后缀在 `group-by`、`sort-by` 中接收函数，这里接收数字，故改名 | 前缀 `section-by` 已在 0.31.0 删除；升级前用 0.30.x 的 `core-function-alias-v1` 迁移直接调用，一等函数引用只给 `requires-review` |
| List `mapcat/.bind: (List<T>, (T)->List<U>) -> List<U>` | **已引入** `.flat-map`；List `.bind` 已在 0.29.0 删除 | 保持顺序、展平一层、callback 次数与具体 U；Fn `.bind` 是不同组合，**暂缓** |
| `.mappend` 在 List/Map/Set/String 上为各自组合，Fn 另有含义 | **已引入** Map `.merge`、Set `.union`，Map/Set 的 `.mappend` 已在 0.29.0 删除；List `.concat` 已存在，但 List `.mappend` 的自动迁移 **暂缓**；String 与 Fn 另行审阅 | 分别验证重复 key 胜出方、去重；List 的包装函数与 `.concat` 参数契约不同，String 现有格式化宽度先核对，不跨 receiver 批量替换 |
| List/String `get/nth` 返回 Option；List `.find/.find-index` 接受 predicate，`.index-of` 接受元素；String `.find-index` 接受子串 | **保留**各命题，List 位置访问使用 `.get`；List `.nth` 已在 0.29.0 删除 | 不把 predicate 查找改成值比较；缺失保持 Option，String 保持标量索引；Enum 异构位置访问 **暂缓** |
| `.map/.filter/.slice/.reverse/.sort/.keys`、`map-entries` | **保留**明确的现有词义 | `map-entries: Map<K,V> -> List<MapEntry<K,V>>` 保留 K/V；不为缩短名字退回异构 List<Dynamic> |

Map/Set 更新均返回新集合，不修改原接收者。Map `.assoc key value` 替换已有 key，`.dissoc key` 忽略不存在的 key；Set `.include item` 去重，`.exclude item` 忽略不存在的元素，二者还接受更多同型元素。空集合遵循相同规则。Set `.add` 与 `.include` 是同一实现；Map `.add ([] key value)` 则先断言 entry 恰为二元 List，非法形状会失败，且无法从 `List<T>` 证明 key/value 分别符合 K/V。因此 Map `.add` 既不是 `.assoc` 的同签名别名，也不使 Map 实现 `Add` trait；`where T: Add` 的泛型调用会拒绝 Map，用户自定义的 `Add` impl 仍按其名义 trait 派发。

0.28.0 保留可精确查询的旧 Map `.add` 作为人工兼容入口，不提供自动改写。#1458 的消费者盘点确认已知活跃下游默认分支没有调用后，0.29.0 删除了该方法，写下它会得到 `E_RETIRED_METHOD` 并提示改用 `.assoc key value`。

历史基线曾出现 `query type "quote $ List Number"` 的 `.reduce` 为 proven、`.append/.foldl` 为 open；目前这些方法已能从公开泛型 schema 获得 proven 契约。seeded `.fold` 也保持 `U, (U,T)->U -> U` 的 proven 方法契约；List 的旧 `.reduce` 已在 0.29.0 删除，自动迁移需用 0.28.x 的 CLI 运行 `core-list-fold-v1`。前缀 `reduce`、自定义 trait 和开放接收者不按名字批量改写，不能用 primitive 替换用户方法测试来绕过。

List/Map/Set 的 `.count` 已在 0.31.0 退场，规则 `core-collection-len-v1` 只在 0.30.x 的 CLI 提供；Struct/Enum 的 `.count` 另有语义，用户自定义 trait 也不据名字猜测；这不是一次全局文本替换。见 [fix 规则](../run/fix.md)。

List 分隔元素的方法迁移曾使用 `core-list-intersperse-v1`（仅 0.28.x 的 CLI 提供），`.join` 方法已在 0.29.0 删除。前缀 `join` 已在 0.31.0 删除；返回 String 的前缀 `join-str` 已在 0.29.0 删除。

List 文本拼接使用 `join-string` / `.join-string`，逐项按原有显示规则转为文本并插入 String 分隔符，空 List 返回空字符串；这不是返回 List 的 `intersperse`。0.29.0 起 `join-string` 直接拥有实现，旧 List `.join-str` 方法与前缀 `join-str` 均已删除；方法的自动迁移需用 0.28.x 的 CLI 运行 `core-list-join-string-v1`，前缀调用人工改写为 `join-string`。

Map 去重值使用 `distinct-values` / `.distinct-values`，返回 `Set<V>`，不是保留重复值的 List。旧 `.values` 方法已在 0.29.0 删除，曾提供的 `core-map-distinct-values-v1` 仅 0.28.x 的 CLI 可用；前缀 `vals` 已在 0.31.0 删除（诊断提示改用 `distinct-values`，规则 `core-function-alias-v1` 仅 0.30.x 的 CLI 可用）；开放接收者与用户方法不按名字改写。

List 展平映射优先使用 `.flat-map`：回调对每个元素调用一次，返回 List 并按顺序展平一层，类型从 `List<T>` 与 `Fn(T)->List<U>` 推断为 `List<U>`。旧 List `.bind` 已在 0.29.0 删除，自动迁移需用 0.28.x 的 CLI 运行 `core-list-flat-map-v1`；前缀 `mapcat` 暂留，Fn `.bind` 不按名字替换。

Map 与 Set 的 `.mappend` 已在 0.29.0 删除，分别改用 `.merge` 与 `.union`；自动迁移需用 0.28.x 的 CLI 运行 `core-collection-combine-v1`。Map 后面的相同 key 覆盖前值，Set 去重；List、String、Fn 的 `.mappend` 保持单独审阅，不做跨类型全局替换。

List 的位置读取使用 `.get`：`List<T>` 的 `.get` 接收 Number 并返回 `Option<T>`，空表或越界返回 `none`；旧 `.nth` 已在 0.29.0 删除。`get` 前缀函数仍承担 Map、开放 Struct 等更广的查找语义。不要据名字把 Map/String/Enum 的 `get/nth` 批量互换，也不要把按 predicate 的 `.find/.find-index` 或按值的 `.index-of` 当作位置读取。

具体 List 的旧 `.nth index` 调用需用 0.28.x 的 CLI 运行 `core-list-get-v1` 迁移到 `.get index`；当前 CLI 只报告 `E_RETIRED_METHOD`。String/Enum 的 `.nth` 不受影响。

```cirru
do
  assert= ([] 1 0 2)
    ([] 1 2) .intersperse 0
  assert= |1-2 $
    [] 1 2
    , .join-string |-
  assert= (#{} 1)
    ({} (:a 1) (:b 1))
      , .distinct-values
  assert= 13 $
    [] 1 2
    , .fold 10 +
  assert= ([] 1 2 3)
    ([] 1 2) .append 3
```

### 转换、解析与名义构造

实施任务：[#1456](https://github.com/calcit-lang/calcit/issues/1456)。`!` 不表示“可能抛错”，`try-` 不表示异步，转换词不能充当类型断言。

0.30.0 的声明、实例化调用与不纳入范围统一见[五个 API 家族的合同边界](../data/README.md#0300-家族范围与排除边界)。查询宏时读取 Macro 的 required/rest/expansion/capabilities，不把源码参数列表当作 runtime arity；查询 typed decoder 时读取实际调用处的闭合 Result 类型，不把宽 builtin 签名当作 payload 已验证。当前冻结验收由 [#1953](https://github.com/calcit-lang/calcit/issues/1953) 跟进，旧名条件退场仍独立由 #1916 承接。

| 当前契约 | 决策 | 类型/失败与迁移限制 |
| --- | --- | --- |
| `Option/Result/Struct` 名义构造；旧 `%some/%none/%ok/%err` 已在 0.31.0 删除 | **保留**直接名义构造 | 完整保留 payload/字段类型与错误；编译器生成的值也直接用名义构造 |
| `turn-string`、`turn-str` 已在 0.31.0 删除；`&turn-string` 仅是六类内建标量的底层实现 | **首选** `to-string: T where T: ToString -> String` | 六类标量和自定义名义 `ToString` 实现可转换；List/Map/Set、Unit、FsPath 和未经解码的 Dynamic 在严格预处理时拒绝。nil 转空串，Tag/Symbol 取文本；已证明的 Number `ToString` 在 native/JS/core WASM/WASI 0.3 共用精确文本语义，包括运行时小数和非有限值。未证明的开放 trait/Dynamic 边界仍明确拒绝，不因 Number 支持而放宽；`to-string` 也不替代 Debug/Show。 |
| `turn-symbol` / `turn-tag` 的历史运行时输入较宽 | **已提供** `to-symbol: String -> Symbol`、`to-tag: String -> Tag`；旧入口暂留 core 宏与兼容路径 | 严格调用只接受 String；native/JS 已验证，WASM 尚无动态 Tag/Symbol intern，调用 `to-tag`/`turn-tag` 会显式报不支持而非伪装成 String。显式 `core-identity-conversion-v1` 只改写内建调用且参数被证明为 String 的稳定源码；也仅对已证明的六类内建标量迁移 `turn-string` 到 `to-string`，Dynamic/集合/自定义类型不自动改。旧入口兼容 Tag/Symbol 输入不构成新接口的静态类型承诺，不可全局替换 |
| `str`、`.debug/.show`、`format-cirru-edn`、`format-to-lisp/to-lispy-string` | **保留**显示/调试/序列化职责；`format-to-lisp` 与 `to-lispy-string` **不等价** | `format-to-lisp` 把代码/list 格式化成 Lisp 表达式，`to-lispy-string` 保留 Calcit 值构造表示：`([] 1 \|a)` 分别得到 `(1 \|a)` 与 `([] 1 \|a)`。二者都不是通用安全转换，不能自动互换；序列化默认 Cirru EDN |
| `.parse-json/.parse-cirru-edn/.parse-cirru/.parse-float` 返回 Result；`json-parse` 等旧入口抛错 | **保留并首选** `.parse-格式` 的 checked 路径；旧 throwing 入口属 **内部兼容** | `String -> Result<T,String>`；开放数据合法保留 Dynamic，typed parse-as 先证明 schema。`parse-float` 的 `NaN`、`inf`、`Infinity` 及带符号、大小写变体在 native/JS 返回 `:ok Number`，无效文本返回原文 `:err`；WASM 若将 `parse-float` 本身列为显式导出或入口 target，会在代码生成阶段因 `E_WASM_NIL_TYPE_EVIDENCE` 失败；若仅作为未支持依赖保留，生成的 stub 被调用时才陷阱，尚不能宣称支持该 Result 路径。throw→Result 是人工迁移，不能自动插入 unwrap/fallback；不新增公开 throwing 别名 |
| `number->int8` 等 `Number -> Result<Refinement,String>`；`js-nullish->option: JsNullish<T> -> Option<T>` | **保留**显式源→目标边界箭头 | 前者检查范围/整数/有限性，后者只包装、不验证 T；不并列再造 `to-/as-/into-` 同义入口，JS 后者仍受 `:js-ffi` 限制 |
| `strip-prefix/strip-suffix: (String,String) -> String`，未匹配保留原串 | **保留**现有签名和不匹配行为 | 不因为 Rust 同名 API 返回 Option 就夹带返回类型变更 |
| `fs:path: String -> FsPath` 与直接 Struct 构造 | **保留** `fs:path` 为应用首选；直接名义构造可追溯 | 两者创建相同 `FsPath :value String`，不访问文件系统、不规范化路径或检查存在；均在严格预处理时拒绝 Number。不新增 `to-path` 或无收益的构造器 fix；不要把内部 `fs-path:*` 当成应用首选 |

这些签名描述值语义，不引入 Rust 的借用或所有权语义，也不承诺零分配。内建 String 的 `to-string` 保留文本值，Number/Bool 转换生成对应文本；格式化与显示操作生成各自的展示文本。名义构造与 checked parser 创建相应结果值，数值 refinement 成功时保留原 Number，不自动舍入。底层共享 String 存储、intern Tag/Symbol 或分配缓冲区属于 backend 实现，不按 `to-/as-/into-` 拼写推断，也不要求不同 backend 具有相同分配次数。

开放解析结果不能直接当作业务类型。已知闭合目标时，优先 `try-parse-cirru-edn-as text TypeExpr`；已取得开放 Calcit 值时，在 native/JS 使用 `try-decode-map-as value TypeExpr`。两者由编译期目标推导 decoder，并在运行时验证 payload，成功后才获得 `Result<T,String>`，不是自动插入 `unsafe-coerce`。前者的 WASM 路径已支持递归闭合集合与 Struct/Enum（含 Option/Result），受输入、集合大小与 32 层 decoder shape 嵌套限制；Map key 可成功解码的类型仅限 nil、Bool、Number/refinement、String、Tag 等标量，集合或 nominal key 以 `E_WASM_EDN_MAP_KEY` 在代码生成阶段拒绝。Unit shape 虽可被代码生成接受，但 Cirru EDN 无 Unit 文本表示，非空 Map 的 Unit key 无法成功解码，不能当作可用的文本边界。shape 过深以 `E_WASM_EDN_DEPTH` 在代码生成阶段拒绝，不与运行时文本超限的 Result 错误混淆。后者不能据此宣称 WASM 支持。具体支持范围见 [WASM 验证说明](../../scripts/wasm-validation.md)，实时 CLI 文档可用 `calcit query def calcit.core/try-parse-cirru-edn-as --format edn` 查看。builtin 原始宽 schema 不等于实例化调用的返回类型，也不能被作为冻结 Dynamic 的理由。

### 效果、宿主与内部实现

实施任务：[#1457](https://github.com/calcit-lang/calcit/issues/1457)；元数据与兼容入口收尾为 [#1458](https://github.com/calcit-lang/calcit/issues/1458)。统一 Calcit-facing adapter，不要求改动外部 JS 属性、native ABI 或 WIT 字段名。

`!` 的目标含义是**显式状态写入、外部动作、资源生命周期与回调注册/取消**。它不是纯度系统、错误标记或权限证明。普通文件/环境/时钟读取和随机数取值不因“不纯”自动加 `!`；消耗输入/等待另列有限例外。持久化集合的 insert/remove 返回新值，不加 `!`。

| 当前入口 | 决策与目标签名原则 | 边界 |
| --- | --- | --- |
| 构造 `atom` / `defatom` | **已提供** `ref` / `defref` 作为 `Ref<T>` 的构造名，与 `type-of` 的 `:ref`、谓词 `ref?` 一致；`atom` 与 `defatom` 已在 0.31.0 删除 | native、JS 与 WASM 共享同一实现与 lowering（WASM 仅支持数值 `defref` 全局，局部 `ref` 明确报告 unsupported）；`core-ref-constructor-v1` 随 `defatom` 退场 |
| `reset!/swap!`；旧 `add-watch/remove-watch` | **已提供** `add-watch!/remove-watch!` 作为 Ref watcher 的首选注册/移除入口；`add-watch!` 自 0.29.0、`remove-watch!` 自 0.31.0 起直接是内建实现，旧 `add-watch` / `remove-watch` 已删除 | Ref 写入返回写入值 `T`；watcher 注册/移除共享原 `Ref<T>`、`Tag`、`Unit` 契约。重复/缺失 key 仍报错，callback 次数不变；本阶段不自动改写未知同名调用 |
| FsPath `.write-text!`；js-ffi `write-text!` | core **已提供** `.write-text!`，旧 `.write-text` 已于 0.29.0 删除；模块 **保留**已有 `!` | core 仍 `(FsPath,String)->Result<Unit,String>`；0.28.x 的 `core-effect-method-v1` 可在类型与来源已证明时改写旧调用；js-ffi 原有 Unit/throw/async 契约不因命名一致而自动统一 |
| FfiTask `.cancel/.cancel-with`；FfiResponse 旧 `.resolve/.reject` | **已提供** `.cancel!/.cancel-with!/.resolve!/.reject!`；FfiTask 与 FfiResponse 旧名均已删除 | 新旧方法共用宿主实现，保持泛型、签名、exactly-once、释放与失败行为；native FFI 生命周期能力仅在 native 提供；JS 调用明确抛出包含操作名的 capability 错误，不把缺失能力当作 Unit 成功；**已退场**：0.31.0 起 FfiTask `.cancel/.cancel-with` 报 `E_RETIRED_METHOD` 并给出 `.cancel!/.cancel-with!`。迁移需在升级前用 0.30.x 的 CLI 运行 `core-effect-method-v1`。 |
| `.read-text/.read-dir/.walk-dir`、`get-args/get-env`；std `read-file!/read-dir!/walk-dir!` | **保留**core 查询名字；std 的首选 `read-file/read-dir/walk-dir` 已随模块 **0.2.36** 首次发布 | 已核验的历史稳定组合为 [std 0.2.37](https://github.com/calcit-lang/calcit.std/releases/tag/0.2.37) 与 Calcit **0.28.0**，保持原参数、返回与失败模型，已验证 native 文本读取、目录枚举、递归读取与 FFI 生命周期。历史 [0.2.36](https://github.com/calcit-lang/calcit.std/releases/tag/0.2.36) 配套 **0.28.0-alpha.3**，其 tag 与预发布工具链要求不随新版本改写 |
| `cpu-time: () -> Number` 实际为单调毫秒；`unix-time-ms` | **已提供** `monotonic-time-ms: () -> Number`；自 0.29.0 起直接是内建时钟，旧 `cpu-time` 已删除，`unix-time-ms` 保留 | 只在同一运行内比较经过时间；native、JS、WASI Preview 1 可用，WASI 0.3 command 时钟仍明确不支持。std `get-time!/get-timestamp` 先核对返回模型再定映射 |
| std `set-timeout/set-interval/on-ctrl-c` | **已提供** `set-timeout!`、`set-interval!`、`on-ctrl-c!`，随 [std 0.2.40](https://github.com/calcit-lang/calcit.std/releases/tag/0.2.40) 预发布，配套 Calcit **0.29.0-alpha.27**；旧名保留为带弃用标记的同一函数引用 | 参数、callback 类型和次数、返回的 `FfiTask`、取消与退出行为不变；仅改 Calcit-facing 名称，不改 native symbol。这是 native 模块能力，不新增 JS/WASM/WASI 支持；迁移见[升级指南](../run/upgrade.md#std-定时器与信号订阅命名) |
| 随机数、ID 生成 | 具体词汇 **暂缓**；不因取值有随机性就统一追加 `!` | 区分产生值与改变资源状态，不按字符串后缀批量处理 |
| `println/echo/eprintln/read-stdin-text/wait-ms` | **保留**这些有限、按名明确的效果例外 | 输出、消费 stdin 和等待仍有真实效果；不是“所有 read-/print- 都自动例外” |
| `non-nil!` 是会失败的检查；旧 helper 中的 `!` | **暂缓**到 checked assertion/unwrap 命名明确 | 不改变失败模型，不将 `.unwrap` 自动插入业务代码 |
| `&trait::new/&impl::new` 对比 `&enum-def:new/&struct-def:new`，`is-spreading-mark?/data-definition-*` | **内部**；先修角色元数据，后有界统一内部拼写 | 不给应用新增 alias；同步注册、macro、typing、lowering 与错误映射，不能为前缀整齐重做编译器 |
| `tuple?/tuple-enum` 迁移报错桩；Option/Result method helper | **内部兼容**，分别退场 | 报错桩与仍被方法引用的实现不是一类；`:internal` 也不能隐藏 Option、数学函数等实际公开能力 |

### 迁移与验证

先选择同一精确发布版本的 CLI 与 `@calcit/procs`，再核对模块自己的版本要求。0.28.x 已提供的迁移桥梁不一定仍在 0.29.0 中：遇到已退役的入口或规则，按[升级指南](../run/upgrade.md#兼容入口的退场节奏)先用已发布的旧 CLI 迁移源码，再升级工具链。上表中的暂缓项不是已提供的推荐入口。

1. 等价 fix 复用已有源码来源与 revision/fingerprint 防护；先 preview，再带 `--expect-revision` 应用，再次 preview 应无可自动应用的旧调用。code、`:tests`、`:examples` 分别记录覆盖或人工处置；未知 macro、遮蔽、函数值、开放 receiver、自定义 trait 不猜。固定改写与 preset 是临时迁移桥梁：完整消费者验收后可在非 patch 版本退场，并从当前 preset 移除对应规则。后续项目级修改优先由 Agent 根据当前合同生成受保护事务，不继续扩展版本 × 规则矩阵或新增顶层入口。
2. 保留 Calcit `:tests` 的用户方法调用，覆盖正常、空值、重复项、类型错误、失败与副作用顺序；Rust/脚本只验证 CLI、host/内存等边界。实际使用的 native/JS/WASM/WASI 路径分别验证，unsupported 明确列出，不能为测试绕过 lowering 改成 native call。
3. Respo 回归事件键、HTML/属性/样式输出与集合转换；js-ffi 回归 effect/JS 边界；std 核对时间/随机/文件模型。文本转换用 `to-string`、文本拼接用 `join-string`；判断 nil 用 `non-nil?`，判断 Option variant 用 `.some?/.none?`。旧 nil 判断不能自动换成 Option 判断。保留输出转义、子节点顺序与 callback 次数，不凭名称替换业务语义。
4. 删除旧入口前同时满足：首选入口具有不弱于旧入口的类型证据；严格检查与相关 backend/真实消费者通过；fix 或人工迁移说明可用；至少一个正式版本的迁移窗口；core method 与待删应用入口解耦。到达版本号不自动授权删除；保留原因写到 issue，不让兼容名永久成为平行推荐。

本表来自 core metadata/method tables、`src/calcit/proc_name.rs`、native/JS builtin 实现、实时 `query type` 与 Respo/js-ffi/std 用法核对。它不是新的运行时 source of truth；实际 API 仍由 schema、解析到的定义和测试决定。消费者验证结果与版本收尾进度记录在关联 issues/PR，不用旧项目片段或规划表代替当前源码证据。

## 可运行的角色示例

`Option` 是名义类型定义；加上 variant 后，同一定义也能直接构造值。类型表达式仍写在 schema 中，不能把 `Option` 裸名当成已构造的值：

```cirru
assert= 3 $ .unwrap $ Option :some 3
```

方法在具体接收者上保留类型关系，失败行为也属于该方法的契约：

```cirru
assert= 3 $
  Option :some 3
  , .unwrap
```

无接收者的解析操作保持命名空间函数形式，其返回值是 Result：

```cirru
assert= (Result :ok 3) (parse-float |3)
```

String 的 `.includes?` 检查子串；`.contains-index?` 在 String 上检查**字符索引**是否有效，不能只凭英文词形把两者当同义词。List 和 Map 的 `.get` 都返回 Option：

```cirru
do
  assert= true $ |abc .includes? |b
  assert= true $ |abc .contains-index? 1
  assert= false $ |abc .contains-index? 3
  assert= (Option :some 2)
    ([] 1 2 3) .get 1
  assert= (Option :some 2)
    ({} (:x 2))
      , .get :x
```

下面是内部 primitive 的定位示例，不是应用代码的推荐写法；它和上面的 `.includes?` 在这个输入上结果相同，但应用应先使用方法：

```cirru
assert= true $ &str:includes? |abc |b
```

## Agent 查询顺序

### 核心 API 稳定与集中迁移

这是 [#1568](https://github.com/calcit-lang/calcit/issues/1568) 的策略基础。**现有契约基线只覆盖部分候选，不代表本页所有目标名都已经发布或冻结。** 上文的“目标”“暂缓”和未通过消费者验收的条目不能成为稳定 API 承诺。使用者先查询已安装版本，维护者再依据发布版证据完成清单。

首批转换、解析与副作用边界的候选证据见 [核心 API 契约基线](../data/README.md)：使用原生 Cirru EDN 保存 schema、名义声明和运行时 arity，检查现有方法派发，并在 CI 对当前及 Git 历史基线比较。它是部分 candidate，不是全量冻结，也不新增用户查询命令或动态类型统计。

冻结的是用户层的调用契约，不是编译器内部表示。审阅后的清单需要覆盖首选类型/构造入口、普通函数、trait 与接收者方法；每项记录公开名字、接收者与参数顺序、optional/rest、泛型和 `where` 约束、返回类型、失败语义及实际 backend 支持范围。`Option` 等名义类型不能因带 `:internal` 而漏掉；方法的 `definition` 只是实现路径，`&` primitive、宏展开 helper 与 lowering 名不因此冻结。core 之外的模块由各自仓库维护版本契约，不用 core 清单冻结 npm、native ABI 或 WIT 的外部名字。

清单应从已有 `query def/context/type --format edn` 的结构化证据整理，而不是新增公开 API registry 或查询命令。函数 schema 与具体 receiver 上的方法契约分别导出：只抓一个函数定义无法证明方法派发、歧义、generic bound 或所有 backend 支持。保留原生 Cirru EDN 的 tag、symbol 与类型表达，不把展示字符串作为签名的唯一证据。清单是发布与 review 的基线，不是另一份驱动运行时的定义。

冻结之后，普通版本可以新增不冲突的能力和修复实现错误，但不改已有首选名字、参数/返回类型或合法输入的契约，不用同名 API 悄悄切换命题。修复跨 backend 不一致时仍须说明可观察变化并验证共享 Calcit `:tests`；不能以“bugfix”为由绕过真实消费者检查。确需改名或 breaking contract 的工作集中到明确标记的迁移版本：同一 PR 提供旧新映射、类型与失败差异、升级指南及可验证的消费者证据。

这不冻结类型推导算法、macro 展开或 backend lowering 的内部实现，也不要求保留不必要的 Dynamic。更精确地推断已有结果、提前拒绝原契约之外的输入，应保留合法调用语义并补严格正/负例；若要收窄原本合法的动态输入或改成功/失败模型，则是明确的契约迁移，不能通过修改“公开”的分类绕过。冻结清单中的 `open/ambiguous` 证据不能伪装成 proven，更不能把宽 schema 的类型漏洞变成长期稳定承诺。

迁移先判断是不是**同一调用契约**。来源可唯一定位、参数/返回与失败行为相同、求值次数/次序不变时，优先用确定性映射交给现有 `fix`；只有映射不足以证明方法/类型关系时才增加证明条件。throw → Result、String → Option、去重 → 保留重复、单位改变或泛型约束收紧不是纯改名：需要人工迁移说明，不自动插入 unwrap、fallback 或 unsafe，也不能通过改名表把任意 signature 变化洗成等价。已退役规则会从当前 preset 移除；需要它们的旧项目先使用升级指南指定的历史 CLI，后续项目级修改复用受保护的结构化事务，不继续扩展版本 × 规则矩阵。

每种任务只推荐一个首选写法。兼容名可以被精确查询，以便旧代码找得到迁移路径，但不能与首选名并列生成新代码。别名记录必须给出首选名、同契约与否、现有 fix/人工步骤、首次提供的正式版本、计划移除的集中版本，以及当前阻塞证据。剩余集中迁移与旧入口的计划退场窗口为 **0.31.0**，不改变已在 0.29.0 删除的入口。0.30.1 仅提前交付缺失值原语的 Option 返回及 `&get-env` 参数变化，不授权其他兼容入口提前删除，见[升级手册](../run/upgrade.md)。这个窗口只确定检查时点，不表示各条目已经完成退场验收；未达到门禁的条目保留并记录具体阻塞。到达计划版本仍须满足 #1451 的发布窗口、真实消费者迁移、严格类型/运行验证与 core 实现解耦条件；内部 helper 的应用兼容入口和方法仍需要的实现目标分别处理。

冻结门禁的验收应覆盖两类失败：删除/改名，以及 schema/receiver/generic/失败契约变化。CI 必须能用受控负例证明未经迁移的变化被拒绝，同时验证合法新增与内部实现重构不被误阻断。显式迁移版本的例外需要同一 PR 的映射和相应证明；只更新 baseline 文件或文档不能自动放行。失败语义与 backend 覆盖仍由 Calcit `:tests`、严格负例及既有 host 测试共同证明，不另建统计 analyzer。

#1568 的策略基础已经关闭，未完成的清单覆盖、别名排期与上述门禁继续由 [#1458](https://github.com/calcit-lang/calcit/issues/1458) 收尾；策略 issue 关闭不表示整个 core 已冻结或所有目标名已发布。core 可先发布经过验证的版本，再以该正式版或明确标记的 alpha 验证模块消费者；milestone 只有在消费者和收尾验收完成、正式发版及中文成果 Discussion 发布后才标记完成，避免“要先关闭 milestone 才能发布、又要先发布才能迁移”的循环。

先查类型再选方法，不要从模糊搜索到的内部定义名猜调用形式。以下任务都有可查询的已证明方法契约；名义类型 `FsPath` 使用限定定义名查询，不要写成不存在的内建类型 `'FsPath`：

| 任务 | 接收者 | 首选应用写法 | 结果契约 |
| --- | --- | --- | --- |
| 查找文本子串 | String | `text .includes? fragment` | Bool，不是字符索引检查 |
| 检查 Map 的 key | Map<K,V> | `entries .contains-key? key` | Bool，key 须为 K |
| 带初值的 List 折叠 | List<T> | `items .fold initial reducer` | U，reducer 为 Fn(U,T) → U |
| 判断 Option 有值 | Option<T> | `value .some?` | Bool，不是非 nil 判断 |
| 写入 UTF-8 文件 | FsPath | `path .write-text! content` | Result<Unit,String>，须处理失败 |

```bash
calcit docs read api-roles.md '逐族命名决策'
calcit docs read api-roles.md '谓词与成员查询'
calcit query type "quote $ Option Number"
calcit query type "'String"
calcit query type "quote $ List Number"
calcit query type "quote $ Map Tag Number"
calcit query type calcit.core/FsPath
calcit query def 'calcit.core/Option' --format edn
calcit query def 'calcit.core/Result' --format edn
calcit query context 'calcit.core/option:unwrap' --format edn
calcit query examples calcit.core/fold
calcit analyze check-examples --ns calcit.core --def fold
```

`docs read` 的默认 guidebook 来自已安装的 `~/.config/calcit/docs`，不是当前工作目录的源码，也不会因重编译 CLI 自动更新。找不到新章节时，先用 `docs sections api-roles.md` 核对已安装文档版本，再按已有文档安装流程更新；不要为查新名字再创建查询入口。开发中的 Markdown 可直接用 `docs check-md <path> --snapshot <snapshot>` 验证，模块文档仍用现有 `--module` 参数查询。

`query type` 的 `proven` 表示当前类型能证明该方法的调用契约；`open` 或 `ambiguous` 不是类型安全的肯定结论。对已由显式 fix 证明为同一实现/签名的别名，查询另给 `preferred` 或 `compatibility` 角色，兼容项可追溯首选名和 fix 规则。human 输出按已证明的首选入口、尚未分类的方法、兼容入口分组；未标角色的方法不应被推断为过期，`open` 的方法也不因角色标为首选就变成已证明。方法旁边的 definition path 用于追踪实现。再用 `query def/context` 读取 `:constructor` 或 `:internal` 标签、schema 与示例；需要机器处理时优先用 `--format edn`。项目代码的类型证据可用 `query type-at` 或 `query context` 查看。

`open` 不一定表示整个方法签名未知。接收者已成功绑定到现有 schema 时，查询保留已声明的参数与返回类型，例如 `calcit query type "'String" --format edn` 中 `.parse-json` 和 `.parse-cirru-edn` 的零参数与 `Result<Dynamic, String>` 返回结构；成功 payload 仍是开放数据，需要在具体使用前解码或收窄，错误分支则已有 String 类型。开放 callback 或集合元素同样按声明展示，不推测为具体类型。缺失 schema、接收者绑定失败或分派歧义时仍不提供签名。结构化 `call-types` 只为 `proven` 方法提供；自动 fix 和核心契约冻结仍必须检查证明状态，不能仅凭签名字段存在就放行。

具体接收者的实现可以提供比开放 trait 声明更精确的泛型关系。例如 `calcit query type "quote $ Option Number" --format edn` 的 `.map` 保留 `Fn<Number,U> → Option<U>`，并列出泛型 U；U 不是 Dynamic，也不是已经选择的业务类型。实现细化须保留 trait 的可证明接收者约束与 feature；闭合 trait 签名不会被覆盖。`Option<Dynamic>` 仍为 open，Agent 应先查询实际调用的 `type-at/context`，不能凭方法名强制把未知 payload 收窄。

结构升级后，使用 `calcit fix --preset core-api-0.28-v1 --format edn` 统一预览已证明的核心 API 叶子改名，再按 revision 应用。`:code` 自动扫描，attached `:tests` / `:examples` 人工核对；重复预览的自动建议应为空，剩余 `requires-review` 不等于已迁移。Option/Result helper 和完整构造调用仍分步迁移，顺序与范围见 [0.28 核心 API 命名迁移](../run/fix.md#028-核心-api-命名迁移)。

真实项目中，Quamolit 的 `quamolit.gpu-scalar-program/first-slot` 以 `get slots 0` 得到 `Option<BoundScalar>`，再调用 `.unwrap`；Timegrass 的 `app.server/main!` 对 `parse-float raw` 的 `Result<Number,String>` 调用 `.unwrap-or 11009`。这两条路径均可在各自 Snapshot 上用 `query context` 找到，再用 `query type "quote $ Option …"` 或 `query type "quote $ Result …"` 检查方法契约。它们说明推荐入口应由返回类型决定，而不是由内部函数名字决定；不要求改变这些项目的源码。

`%ok/%err` 从 0.25.0 起标记弃用，在核心库完成迁移后于 0.31.0 与 `%some/%none` 一起删除，编译器生成的值改为 `Result :ok/:err` 与 `Option :some/:none`。旧项目先用 0.30.x 的 CLI 运行 `core-nominal-constructor-v1` 预览并应用，见[升级指南](../run/upgrade.md#0310-删除最后一批已弃用的核心名字)。String 旧 `.contains?` 检查索引而 `.includes?` 检查子串；该旧方法已在 0.29.0 删除，自动迁往 `.contains-index?` 需用 0.28.x 的 CLI 运行 `core-predicate-method-v1`，不能在跨容器调用上按词形全局替换。

对 Option 的旧 helper，可用 `calcit fix --rule core-option-method-v1 --format edn` 预览 `.some?` / `.none?` / `.unwrap` / `.unwrap-or` 迁移。只有接收者方法解析为 `proven` 且实现确实指向相同 core helper 时才提供自动改写；已核实单次保留调用的 core `let`、`cond`、`do`、`fn`、`assert=` 宏允许通过，其他宏仍需审阅。已证明来自 core 的空值 `Option :none` 即使起初显示 `Option<Dynamic>`，也可由具体 fallback 推出 payload 类型，再检查方法分派；普通开放接收者、函数值或无来源映射的表达式仍不能自动改写。应用后重复预览应为空，并运行严格类型检查与项目测试；不把 String 或其他内部函数类推为同一个迁移。

Result 的 `result:ok?` / `result:err?` / `result:unwrap-or` 沿用单独的 `core-result-method-v1` 规则，不新增顶层 CLI 入口。只有编译器能证明同一方法契约才自动迁移；已证明来自 core 的 `Result :err` 不携带成功值，因此允许具体 fallback 绑定成功类型，同时仍检查错误类型。成功值已有具体类型的 core `Result :ok` 可按同一方法契约迁移；普通开放 Result 和 `:ok` 的动态成功值不能凭 fallback 收窄，具名构造缺少精确源码证据时也保留审阅。旧 helper 作为 core 实现暂留；公开文档只推荐 `.ok?` / `.err?` / `.unwrap-or`。在真实消费者完成迁移、Agent 查询和升级文档统一首选写法、经过至少一个发布窗口且 JS/native/WASI 相关测试通过之前，不删除旧 helper。其他 `result:*` 逐项确认语义与迁移证据，不跟随批量废弃。

### 旧方法 helper 的退场条件

从 0.26.0 开始收敛**应用源码的首选入口**；0.29.0 仍保留 `option:some?`、`option:none?`、`option:unwrap`、`option:unwrap-or`、`result:ok?`、`result:err?`、`result:unwrap-or`。这些名字目前也是 core trait method 的实现目标；直接删除会破坏推荐的 `.method`，而不是仅移除旧别名。旧 helper 暂作内部兼容实现，不把它们和方法并列推荐，也不把内部调用数量当作应用迁移进度。

剩余集中迁移与这些**应用可直接调用**的兼容入口以 **0.31.0** 为计划退场窗口，且须同时满足：真实消费者在发布版 Calcit 与匹配的运行时依赖上完成严格类型和运行测试；对消费者 Snapshot 重复运行对应 fix 无可自动改写的旧调用，剩余 `requires-review` 已逐一处置；Agent 查询、升级文档与示例只推荐方法；至少经历一个已发布版本的迁移窗口；JS/native 及实际受影响的 WASM/WASI 路径验证通过；core method 实现已与将删除的入口解耦，并有 Calcit `:tests` 证明行为不变。计划版本本身不证明这些条件已满足；任一条件未满足就继续保留兼容入口，记录原因和下一次检查的版本，不通过扩大 Dynamic 或机械替换绕过。此约束不适用于无自然接收者的模块函数，也不承诺把所有 `result:*` / `option:*` 内部实现一并移除。

`%ok/%err`、`defatom`、`remove-watch`、`turn-string`、`let{}`、`let-destruct` 已在 0.31.0 删除，改写方式见[升级指南](../run/upgrade.md#0310-删除最后一批已弃用的核心名字)；`some-in?` 与 `range-bothway` 见[删除已弃用的兼容函数](../run/upgrade.md#0310-删除已弃用的兼容函数)。改写 `let{}` 时原 Map 只求值一次；改为公开 `get` 后须人工审阅 Option 与旧 nil/缺失行为，不能自动插入 unwrap 或业务默认值。已知 Struct 字段用 `(:field value)`，不是 JS 属性访问 `.-field`。

### 最终值存在性

`.ok? $ get-in data path` 检查最终值是否非 nil，而不是检查值的真假：false、0 和空字符串
仍是存在的值。Map 中保存 nil 的 key 可以满足 `contains-in?`，但最终值检查为 false；
缺失路径则两者都为 false；中间值不是集合时 `.ok?` 也为 false。

## 限制

- std 0.2.37 发布的是源码模块，不代表预编译 dylib 已发布。
- 当前发布版组合的回归验证不代表所有下游已完成迁移。
- `get-in` 遍历混合值 Map 后的开放 nil 判断目前在 WASM/WASI 报 `E_WASM_NIL_TYPE_EVIDENCE`，不表示该路径已具备跨后端支持。
