@calcit/procs 0.13.27 → 0.13.28

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. package/.yarn/install-state.gz +0 -0
  2. package/README.md +34 -34
  3. package/RFCs/02-04-runtime-traits-plan.md +1 -1
  4. package/RFCs/02-17-register-platform-api-rfc.md +1 -1
  5. package/RFCs/02-18-language-theory-evolution-plan.md +3 -3
  6. package/RFCs/03-05-function-schema-dual-track-rfc.md +3 -3
  7. package/RFCs/03-18-query-def-tree-show-chunked-display-plan.md +11 -11
  8. package/RFCs/04-13-type-slot-mechanism-rfc.md +10 -10
  9. package/RFCs/05-12-program-diff-rfc.md +7 -7
  10. package/RFCs/06-15-effects-graph-rfc.md +16 -16
  11. package/RFCs/07-06-semantic-tree-navigation-rfc.md +33 -33
  12. package/RFCs/07-19-doc-knowledge-index-rfc.md +15 -15
  13. package/RFCs/07-19-type-introspection-consistency-rfc.md +3 -3
  14. package/RFCs/07-26-agent-docs-and-evaluation-rfc.md +1 -1
  15. package/RFCs/07-26-agent-machine-protocol-rfc.md +5 -5
  16. package/RFCs/07-26-safe-structured-editing-rfc.md +4 -4
  17. package/RFCs/07-26-static-semantic-analysis-rfc.md +9 -9
  18. package/RFCs/07-28-git-module-store-rfc.md +4 -4
  19. package/RFCs/07-28-persistent-tree-cursor-rfc.md +54 -54
  20. package/RFCs/08-08-cross-backend-host-ffi-contracts-rfc.md +4 -4
  21. package/RFCs/08-14-architecture-scaffold-rfc.md +7 -7
  22. package/RFCs/08-14-todo-placeholder-rfc.md +2 -2
  23. package/RFCs/08-18-calcit-typed-js-ffi-boundary-rfc.md +2 -2
  24. package/RFCs/08-21-js-ffi-runtime-contract-validation-rfc.md +295 -0
  25. package/RFCs/08-21-setup-calcit-version-and-toolchain-contract-rfc.md +241 -0
  26. package/RFCs/08-21-static-type-system-evolution-roadmap.md +315 -0
  27. package/RFCs/08-21-type-quality-ci-adoption-rfc.md +272 -0
  28. package/RFCs/README.md +7 -4
  29. package/editing-history/20260820-1704-repository-maintenance-review.md +20 -0
  30. package/editing-history/20260821-1512-type-system-ecosystem-rfcs.md +25 -0
  31. package/editing-history/20260821-1648-type-quality-and-ffi-doc-hints.md +12 -0
  32. package/editing-history/202608211958-calcit-command-alias.md +6 -0
  33. package/editing-history/202608212018-cli-review-followups.md +4 -0
  34. package/editing-history/ARCHIVE.md +45 -4
  35. package/editing-history/README.md +3 -1
  36. package/history/202608212020-release-01328.md +5 -0
  37. package/lib/package.json +11 -11
  38. package/package.json +11 -11
  39. package/profiling/README.md +3 -3
  40. package/profiling/profile-once.sh +1 -1
  41. package/profiling/samply-once.sh +4 -4
  42. package/profiling/samply-summary.py +1 -1
  43. package/editing-history/2026-07-28-1346-edit-transaction.md +0 -27
  44. package/editing-history/2026-07-28-1454-persistent-tree-cursor.md +0 -30
  45. package/editing-history/2026-07-28-1651-cursor-focus-stack-clipboard.md +0 -30
  46. package/editing-history/2026-07-28-1719-cursor-navigation-search-selection.md +0 -21
  47. package/editing-history/2026-07-28-1958-agent-rfc-split.md +0 -14
  48. package/editing-history/2026-07-28-2004-cursor-editing-docs.md +0 -14
  49. package/editing-history/2026-07-28-2132-cursor-recoverable-clipboard.md +0 -18
  50. package/editing-history/2026-07-28-2151-cursor-native-structural-editing.md +0 -20
  51. package/editing-history/2026-07-28-2158-document-cursor-native-workflows.md +0 -13
  52. package/editing-history/2026-07-29-0022-add-cursor-cli-options.md +0 -7
  53. package/editing-history/2026-07-29-0022-audit-cursor-development-scenarios.md +0 -6
  54. package/editing-history/2026-07-29-0022-complete-cursor-structural-edits.md +0 -8
  55. package/editing-history/2026-07-29-0022-document-cursor-edit-recipes.md +0 -8
  56. package/editing-history/2026-07-29-0022-edit-target-cursor-alias.md +0 -6
  57. package/editing-history/2026-07-29-0022-guide-agents-through-cursor-workflows.md +0 -7
  58. package/editing-history/2026-07-29-0022-query-from-active-cursor.md +0 -8
  59. package/editing-history/2026-07-29-0022-tree-target-cursor-alias.md +0 -5
  60. package/editing-history/202608021730-release-0.12.58.md +0 -5
  61. package/editing-history/202608021928-release-0.12.59.md +0 -5
  62. package/editing-history/202608082312-release-0.13.5.md +0 -10
  63. package/editing-history/202608090107-release-0.13.6.md +0 -10
  64. package/editing-history/202608091812-release-0.13.7.md +0 -8
  65. package/editing-history/202608101042-release-0.13.8.md +0 -6
  66. package/editing-history/202608101101-release-pr-workflow.md +0 -8
  67. package/editing-history/202608101102-release-doc-tracked-path.md +0 -5
  68. package/editing-history/202608110140-release-0.13.9.md +0 -4
  69. package/editing-history/202608111349-release-0.13.10.md +0 -13
  70. package/editing-history/202608111650-release-0.13.11.md +0 -5
  71. package/editing-history/202608121900-release-0.13.12.md +0 -5
  72. package/editing-history/202608130003-release-0.13.13.md +0 -9
  73. package/editing-history/202608131650-release-0.13.15.md +0 -10
  74. package/editing-history/20260816-0254-release-0.13.17.md +0 -5
  75. package/editing-history/20260817-0006-release-0-13-18.md +0 -6
  76. package/editing-history/202608171118-project-module-runtime-resolution.md +0 -6
  77. package/editing-history/202608171133-visible-module-versions-and-cleanup.md +0 -6
  78. package/editing-history/202608171140-module-caches-root.md +0 -5
  79. package/editing-history/202608171200-module-cache-review-follow-up.md +0 -5
  80. package/editing-history/202608171205-release-0-13-19.md +0 -4
  81. package/editing-history/202608171215-module-path-resolution-docs.md +0 -4
  82. package/editing-history/202608171409-module-cache-review-followup.md +0 -5
  83. package/editing-history/202608171427-module-cache-review-followup-locking.md +0 -5
  84. package/editing-history/202608171438-module-docs-home-lock.md +0 -4
  85. package/editing-history/202608171611-dependency-audit-review.md +0 -4
  86. package/editing-history/202608181000-release-0-13-20.md +0 -5
  87. package/editing-history/202608181930-release-0-13-21.md +0 -5
@@ -41,7 +41,7 @@ JavaScript 宿主对象不建立一套类似 TypeScript 的结构化类型系统
41
41
  2. 用现有 trait 体系描述少量稳定宿主操作。
42
42
  3. 让 codegen 能从声明确定应生成属性读取、属性写入还是宿主方法调用。
43
43
  4. 保留 `JsObject` 与 `JsNullish<T>` 的不透明边界语义。
44
- 5. 让 Snapshot 中的声明可由 `cr query`、生成器和静态分析直接读取。
44
+ 5. 让 Snapshot 中的声明可由 `calcit query`、生成器和静态分析直接读取。
45
45
  6. 保持声明足够小,使绑定模块只描述应用实际使用的 API。
46
46
 
47
47
  ### 3.2 非目标
@@ -477,7 +477,7 @@ MVP 建议使用少量稳定诊断:
477
477
  3. 现有手写 JS binding 可以继续使用普通 `defn`;只有需要生成或查询宿主绑定时才增加 `:ffi`。
478
478
  4. `defwasm-import`、`defwasm-export` 与 registered proc 先只在 query 中暴露规范化 metadata,不立即改变运行时。
479
479
  5. 旧的 `:string`、`:: :fn` 等 schema 输入仍可读取;重新保存和新生成内容使用 quoted-symbol 形式。
480
- 6. Snapshot loader 在正式写入 `:ffi` 前必须先做到未知字段无损保留,或明确实现 `CodeEntry.ffi`;不能让一次 `cr edit` 静默删除 binding 元数据。
480
+ 6. Snapshot loader 在正式写入 `:ffi` 前必须先做到未知字段无损保留,或明确实现 `CodeEntry.ffi`;不能让一次 `calcit edit` 静默删除 binding 元数据。
481
481
 
482
482
  ## 12. 实施阶段
483
483
 
@@ -493,7 +493,7 @@ MVP 建议使用少量稳定诊断:
493
493
  - 支持 JS `:kind :import` 的 `:function`、`:method`、`:value`;
494
494
  - 从 definition 的 `Fn` schema 检查 arity、参数和返回值;
495
495
  - 要求 JS binding schema 带 `:features #{:js-ffi}`;
496
- - 在 `cr query context` 和机器 JSON 中暴露 `schema` 与 `ffi`,但不把二者合并。
496
+ - 在 `calcit query context` 和机器 JSON 中暴露 `schema` 与 `ffi`,但不把二者合并。
497
497
 
498
498
  ### 阶段 2:JS external trait
499
499
 
@@ -520,7 +520,7 @@ Snapshot/EDN 测试:
520
520
  - `Fn`、`Trait` 与 named type reference 按规范形式 round-trip;
521
521
  - `:ffi` 未知扩展字段无损保存;
522
522
  - Cirru EDN 中 map、list、set、string、symbol 和 tag 不混淆;
523
- - `cr edit schema` 只更新 `:schema`,不删除或改写 `:ffi`。
523
+ - `calcit edit schema` 只更新 `:schema`,不删除或改写 `:ffi`。
524
524
 
525
525
  类型测试:
526
526
 
@@ -11,8 +11,8 @@
11
11
  建议命令:
12
12
 
13
13
  ```bash
14
- cr edit scaffold --file docs/architectures/order-submission.cirru --dry-run --format edn
15
- cr edit scaffold --file docs/architectures/order-submission.cirru --expect-revision '<revision>'
14
+ calcit edit scaffold --file docs/architectures/order-submission.cirru --dry-run --format edn
15
+ calcit edit scaffold --file docs/architectures/order-submission.cirru --expect-revision '<revision>'
16
16
  ```
17
17
 
18
18
  它与现有编辑原语的关系是:
@@ -227,7 +227,7 @@ defn validate-order (order)
227
227
  ### 4.1 主命令
228
228
 
229
229
  ```bash
230
- cr edit scaffold --file <architecture.cirru> [options]
230
+ calcit edit scaffold --file <architecture.cirru> [options]
231
231
  ```
232
232
 
233
233
  第一版参数:
@@ -409,9 +409,9 @@ Snapshot-level `--expect-revision` 安全但保守:两个 Agent 修改不同 d
409
409
  cursor user 用于区分 CLI 导航状态,不是 work item owner、权限身份或并发锁。parent 可以为 subagent 设置:
410
410
 
411
411
  ```bash
412
- cr --cursor-user agent-a cursor set app.order/validate-order --path @0
413
- CALCIT_CURSOR_USER=agent-b cr cursor show
414
- cr cursor show # fallback: default
412
+ calcit --cursor-user agent-a cursor set app.order/validate-order --path @0
413
+ CALCIT_CURSOR_USER=agent-b calcit cursor show
414
+ calcit cursor show # fallback: default
415
415
  ```
416
416
 
417
417
  解析优先级仍为 `--cursor-user` > `CALCIT_CURSOR_USER` > `default`。
@@ -481,7 +481,7 @@ CLI 仍接受任意 `--file`。目录只是约定,不由第一版自动创建
481
481
  - JSON compatibility projection;
482
482
  - 不写 Snapshot 的单元与 CLI 协议测试。
483
483
 
484
- 当前实现进度(2026-08-14):`cr edit scaffold` 已支持 `--file`、`--code` 或 stdin 输入,以及 `human`、`edn`、`json` 输出。它验证 Symbol FQN/params、anonymous-enum edge、schema、roots/edge endpoint,计算 `plan-id`,对当前 Snapshot 做 create/reuse/external reconciliation,并输出 create operation 预览和 work items。`--dry-run` 保持只读;不带该 flag 时,会在同目录 staged file 中创建全部缺失的 `:ensure` definition,写入 doc/schema、`:scaffold` tag 和 function `todo!` stub,复核 revision 后以 atomic rename 提交。已有 definition 从不被覆盖;成功 apply 后会触发既有 cursor 后置校验。dependency/core external lookup、definition-level patch 和 per-user cursor 仍留在后续阶段。
484
+ 当前实现进度(2026-08-14):`calcit edit scaffold` 已支持 `--file`、`--code` 或 stdin 输入,以及 `human`、`edn`、`json` 输出。它验证 Symbol FQN/params、anonymous-enum edge、schema、roots/edge endpoint,计算 `plan-id`,对当前 Snapshot 做 create/reuse/external reconciliation,并输出 create operation 预览和 work items。`--dry-run` 保持只读;不带该 flag 时,会在同目录 staged file 中创建全部缺失的 `:ensure` definition,写入 doc/schema、`:scaffold` tag 和 function `todo!` stub,复核 revision 后以 atomic rename 提交。已有 definition 从不被覆盖;成功 apply 后会触发既有 cursor 后置校验。dependency/core external lookup、definition-level patch 和 per-user cursor 仍留在后续阶段。
485
485
 
486
486
  ### Phase 2:atomic scaffold apply(基础版本已实现)
487
487
 
@@ -89,8 +89,8 @@ Cirru EDN diagnostic 至少包含:
89
89
 
90
90
  - `W_TODO` 是 completion warning,不是 type warning;同一位置不得再产生伪造的返回类型 mismatch;
91
91
  - scaffold dry-run/apply 把它列入 expected warnings,不把新 stub 当成 apply conflict;
92
- - `cr --check-only` 遵循当前严格 warning 策略:可创建 scaffold,但仍有 reachable TODO 时检查返回非零,Agent 不能宣称功能完成;
93
- - `cr analyze check-types` 扫描所选 Snapshot definition,将 TODO 数量和 diagnostics 纳入 human/Cirru EDN 报告,即使节点暂时不从 entry 可达;
92
+ - `calcit --check-only` 遵循当前严格 warning 策略:可创建 scaffold,但仍有 reachable TODO 时检查返回非零,Agent 不能宣称功能完成;
93
+ - `calcit analyze check-types` 扫描所选 Snapshot definition,将 TODO 数量和 diagnostics 纳入 human/Cirru EDN 报告,即使节点暂时不从 entry 可达;
94
94
  - 后续若引入 warning severity/allow-list,`W_TODO` 的默认 completion gate 仍应为 deny,显式探索性运行才允许降级;
95
95
  - Cirru EDN stdout 保持单个 value;JSON 只作为现有工具兼容投影,human warning 与普通命令提示走 stderr。
96
96
 
@@ -849,7 +849,7 @@ availability。Backend 信息不参与普通 schema type matching,但调用不
849
849
  definition 的 `:ffi` metadata 可以声明 `(:target :browser)` 或
850
850
  `(:target :node)`。typed external-object operation 和带该 metadata 的 raw host
851
851
  wrapper 会在 codegen 前检查 selected entry;缺少 entry target 的旧项目保持兼容,暂不做
852
- target-specific validation。`cr query def ns/name --json` 同时暴露 `ffi` metadata,包含
852
+ target-specific validation。`calcit query def ns/name --json` 同时暴露 `ffi` metadata,包含
853
853
  host name mapping,供 adapter 审计使用。
854
854
 
855
855
  ## external trait 的有限补充
@@ -964,7 +964,7 @@ generic trait、associated type、effect row 和 FFI 专用 type constructor 都
964
964
  2. `js-ffi` 公共 wrapper 不向业务代码暴露无理由的 `Dynamic`。
965
965
  3. probe、错误和状态结果使用命名 Struct/Enum。
966
966
  4. `JsNullish<T>` 不会静默匹配 `Option<T>`,存在性检查后仍保留 payload type。
967
- 5. external field/method 的类型和 host name 可由 `cr query` 查询。
967
+ 5. external field/method 的类型和 host name 可由 `calcit query` 查询。
968
968
  6. 未声明 writable 的 external field 无法被 typed write。
969
969
  7. 带 `:js-ffi` 的匿名 callback 能保留实现体归属,capability validation 能检查其 raw
970
970
  operation;Fn 类型匹配结果不因 feature 改变。
@@ -0,0 +1,295 @@
1
+ # RFC:JS FFI 静态声明与运行时契约验证
2
+
3
+ 状态:Draft
4
+
5
+ 日期:2026-08-21
6
+
7
+ ## 摘要
8
+
9
+ Calcit 的 JS FFI 已能用 `JsObject`、`JsNullish<T>`、完整 Fn schema、external-object trait
10
+ 和 `:features #{:js-ffi}` 描述边界,但“类型检查通过”目前主要证明 Calcit 侧的声明自洽,不能
11
+ 证明 JavaScript 宿主真的满足声明。
12
+
13
+ 本 RFC 补充静态类型与实际宿主之间的验证闭环:
14
+
15
+ 1. 将 host import、property/method、callback、exception、Promise 和 `unsafe-coerce` 视为需要
16
+ 单独证据的 contract boundary;
17
+ 2. 在 `js-ffi` 库提供可复用的 guard/decoder/normalizer,不让每个应用自行断言;
18
+ 3. 为 Node 与 browser 建立正向、负向 runtime contract test;
19
+ 4. 将 unchecked host assertion 纳入原生质量报告,但不把 runtime test 冒充静态分析;
20
+ 5. 保持现有名义类型与 FFI metadata 设计,不引入 TypeScript 式结构类型系统。
21
+
22
+ 本 RFC 是 `08-18-calcit-typed-js-ffi-boundary-rfc.md` 的后续。前者解决“如何声明和授权”,
23
+ 本 RFC 解决“声明如何被宿主运行证据支持”。
24
+
25
+ ## 问题定义
26
+
27
+ 下面的代码可以在静态层拥有精确返回类型:
28
+
29
+ ```cirru.no-check
30
+ defn viewport-width () $ unsafe-coerce js/window.innerWidth Number
31
+ ```
32
+
33
+ 但 schema 本身不能证明:
34
+
35
+ - 当前 target 一定有 `window`;
36
+ - `innerWidth` 存在且是 number;
37
+ - npm 包仍导出声明中的 symbol;
38
+ - 取出的函数是否需要 JavaScript `this`;
39
+ - callback 会收到声明的参数;
40
+ - Promise 不会 reject;
41
+ - `null`/`undefined` 没有被误断言为普通值;
42
+ - 已生成代码与当前 `@calcit/procs` runtime 相容。
43
+
44
+ 因此应明确:
45
+
46
+ > FFI schema 是 Calcit 对宿主的契约声明,不是宿主事实的自动证明。
47
+
48
+ 在纯 Calcit typed code 中,“类型通过但发生普通类型错误”应被视为编译器问题;在 FFI 边界,
49
+ 同类失败首先说明 contract 没有被验证、第三方 API 漂移,或使用了显式 unsafe escape。
50
+
51
+ ## 失败分类
52
+
53
+ | 类别 | 例子 | 首选发现阶段 |
54
+ | --- | --- | --- |
55
+ | target mismatch | browser binding 用于 Node | codegen 前静态检查 |
56
+ | missing binding | npm named export 被移除 | module load/runtime contract test |
57
+ | wrong primitive | 声明 Number,宿主返回 String | boundary guard |
58
+ | nullish mismatch | `undefined` 被断言成 String | boundary guard/decoder |
59
+ | object capability | method 不存在或不是 function | external-object assertion/test |
60
+ | receiver loss | 取出 method 后无 `this` 调用 | lowering test |
61
+ | callback mismatch | 宿主参数或 callback 返回值不符 | callback adapter contract test |
62
+ | exception/rejection | API throw 或 Promise reject | adapter 转 Result + negative test |
63
+ | runtime identity | 生成代码引用旧/缺失 proc | JS runtime identity test |
64
+
65
+ 静态分析应尽早拒绝可证明的问题;需要观察宿主值的问题由 boundary validation 和 runtime test
66
+ 承担。不能因为后者无法在编译期证明,就整体退回 `Dynamic`。
67
+
68
+ ## 设计原则
69
+
70
+ ### unsafe 必须可见
71
+
72
+ `unsafe-coerce` 继续表示“维护者提供可信证据”,不偷偷增加运行时开销,也不伪装成 decoder。
73
+ 但 compiler/query/quality 必须能够报告:
74
+
75
+ - source type;
76
+ - target type;
77
+ - definition 与 Snapshot path;
78
+ - 所在函数是否具有 `:js-ffi`;
79
+ - 是否位于被声明为 raw adapter 的 namespace/definition;
80
+ - 是否紧邻一个已识别 guard/decoder。
81
+
82
+ 从宿主值直接 coercion 到 Struct/Enum、primitive 或精确 Fn schema 是高风险项。它可以存在,但
83
+ 必须被审计,且不能在普通业务 namespace 扩散。
84
+
85
+ ### guard、decoder、normalizer 分层
86
+
87
+ `js-ffi` 提供三类可复用能力:
88
+
89
+ 1. **guard**:验证 primitive、nullish、array、function、必要字段/方法等浅层宿主事实;
90
+ 2. **decoder**:递归验证外部数据并返回 `Result<T, JsError>`;适合 JSON/object 数据;
91
+ 3. **normalizer**:捕获 exception/rejection,把宿主对象转换为 Struct/Enum/List/Map/Option/
92
+ Result/Unit。
93
+
94
+ 具体 API 名称可以在实现时确定,但语义必须区分:guard 证明一个有限事实,decoder 产生
95
+ Calcit-owned data,`unsafe-coerce` 只接受维护者断言。
96
+
97
+ `decode-map-as` / 严格 Cirru EDN decoder 已证明“按目标类型派生验证器”可行;JS object decoder
98
+ 应尽量复用同一 data-shape 规则和错误路径格式,而不是在 js-ffi 内维护另一套类型 AST。
99
+
100
+ ### external trait 是能力,不是完整对象验证
101
+
102
+ external-object trait 声明调用者会使用的稳定字段和方法。对一个 `JsObject` 建立该 trait
103
+ evidence 时,测试/调试模式至少可以验证:
104
+
105
+ - 声明为 method 的成员存在且为 function;
106
+ - 必需字段存在;
107
+ - 可廉价验证的 primitive 字段类型正确;
108
+ - writable metadata 不包含未知字段。
109
+
110
+ 这不是 TypeScript structural satisfaction,也不生成普通 runtime impl。宿主对象后续仍可能
111
+ 变化;真正需要稳定数据不变量时应复制并 decode 成 Struct/Enum。
112
+
113
+ ### exception 与 async 是契约的一部分
114
+
115
+ 可能 throw/reject 的 API 不得只声明成功返回类型。公共 adapter 返回
116
+ `Result<T, JsError>`;不存在与失败分别用 `Option` 和 `Result` 表达。未 await 的 Promise 不以
117
+ 裸 `JsObject` 泄漏到业务层。
118
+
119
+ ## 编译器与分析改进
120
+
121
+ ### Host contract evidence
122
+
123
+ 在现有 `HostOperation`/FFI metadata 基础上,为边界产生统一 evidence:
124
+
125
+ - binding target 与 host name;
126
+ - operation kind:import/value/property/method/constructor/callback/await;
127
+ - logical schema;
128
+ - target:browser/node/neutral;
129
+ - assertion kind:checked/decoded/unsafe;
130
+ - definition/path。
131
+
132
+ 它是类型检查后的 contract analysis,不参与普通 `matches_with_bindings`、泛型统一或 trait
133
+ candidate selection。
134
+
135
+ ### 诊断
136
+
137
+ 建议新增或稳定以下诊断:
138
+
139
+ - `E_JS_FFI_TARGET_MISMATCH`:selected entry 与 binding target 冲突;
140
+ - `E_JS_FFI_FEATURE_REQUIRED`:实现体没有授权 host operation;
141
+ - `W_JS_FFI_UNCHECKED_COERCE`:宿主值直接转换到强类型且没有可见验证;
142
+ - `W_JS_FFI_UNTESTED_BINDING`:公共 binding 没有 contract example/test evidence;
143
+ - `E_JS_FFI_RUNTIME_CONTRACT`:调试/测试 guard 发现实际值与声明不一致。
144
+
145
+ runtime error 至少携带 Calcit definition、host binding/member、expected、actual kind 和 entry
146
+ target。不要只暴露 JavaScript `Cannot read properties of undefined`。
147
+
148
+ ### Quality 集成
149
+
150
+ `analyze quality` 后续增加独立维度,例如 unchecked host assertions 与 untested public
151
+ bindings。它们进入同一版本化 JSON/baseline 协议,不由项目自写 JS 扫描 generated code。
152
+
153
+ quality 输出只声明“发现/未发现静态契约风险”,不能声称 Node/browser runtime tests 已执行。
154
+ 后端 test 仍是单独 CI step。
155
+
156
+ ## `js-ffi` 库改进
157
+
158
+ ### 模块组织
159
+
160
+ 建议稳定三层目录/namespace:
161
+
162
+ - `js-ffi.raw.*`:唯一允许 raw `js/*` 和必要 `unsafe-coerce` 的实现层;
163
+ - `js-ffi.host.*`:小型 external traits 与 checked host wrappers;
164
+ - `js-ffi.*` / `js-ffi.types`:normalized public API、`JsError` 和业务可用数据。
165
+
166
+ 已有公开路径可以通过 re-export 保持兼容,不要求一次性改名。
167
+
168
+ ### 优先补强的应用场景
169
+
170
+ 1. `globalThis`、Node `process`、browser `window/document` 的 target detection;
171
+ 2. npm/ES module function import,特别是 default/named export 与 receiver method;
172
+ 3. DOM query、event target 和 listener callback;
173
+ 4. timer、storage、clipboard 等 effect API 的 Unit/Result 契约;
174
+ 5. Promise resolve/reject 与 async callback;
175
+ 6. host Array/object 到 `List<T>`、Struct、Enum 的 decoder;
176
+ 7. `@calcit/procs` generated-code runtime identity。
177
+
178
+ 每个场景先选真实应用调用路径,不为未使用的完整 DOM/npm surface 建模。
179
+
180
+ ## Runtime contract test 矩阵
181
+
182
+ ### Node
183
+
184
+ - 生成实际 JS;
185
+ - 使用项目声明的 `@calcit/procs` 版本执行;
186
+ - 调用每个公共 Node binding 的最小成功用例;
187
+ - 对 missing export、错误 primitive、throw、rejection 和 nullish 运行负向 fixture;
188
+ - 验证错误包含 Calcit binding identity,而不只是原生 JS stack。
189
+
190
+ ### Browser
191
+
192
+ - 在 headless browser 装载真实生成物;
193
+ - 对 DOM property/method、receiver binding、event callback 和 writable field 做 smoke test;
194
+ - 在没有目标 API或返回 nullish 时验证 Option/Result;
195
+ - Node-only binding 在 browser target 于 codegen 前失败,反向亦然。
196
+
197
+ ### Generated runtime identity
198
+
199
+ - codegen 使用的 proc export 在当前 `@calcit/procs` 中全部存在;
200
+ - compile-time-only form 不泄漏为 runtime proc;
201
+ - Calcit CLI 与 npm runtime 版本不同时给出明确兼容错误或由项目版本策略阻止;
202
+ - contract test 执行 generated JS,而不是只检查文本中是否出现某个 symbol。
203
+
204
+ ### Callback
205
+
206
+ 至少覆盖:
207
+
208
+ - primitive/Struct callback 参数正常;
209
+ - nullish 参数按声明转换;
210
+ - callback 返回 Unit;
211
+ - callback throw 被保留为明确错误或按 adapter 契约转 Result;
212
+ - async callback rejection;
213
+ - escaping callback 的 `:js-ffi` lexical capability 没有丢失。
214
+
215
+ ## 运行时检查策略
216
+
217
+ 不是所有 external field access 都需要永久重复 `typeof`。建议分三种模式:
218
+
219
+ | 模式 | 行为 | 使用场景 |
220
+ | --- | --- | --- |
221
+ | boundary | 只为一次 decoder 操作检查并立即复制为 Calcit-owned data | 默认生产路径 |
222
+ | debug | boundary 外加关键 callback/返回值断言 | 测试、开发、生态升级 |
223
+ | unsafe | 只保留明确 `unsafe-coerce`,由审计和 contract test 承担 | 性能敏感且已证明的 adapter |
224
+
225
+ 模式属于 entry/build policy,不进入普通类型身份。即使选择 unsafe,target 和 capability gate 仍需
226
+ 静态检查;关闭 runtime guard 不等于允许未授权 FFI。
227
+
228
+ ### evidence 的有效范围
229
+
230
+ host object 默认是可变的,不能把一次检查当成可无限期复用的“已验证外部对象”能力。boundary
231
+ 模式的 evidence 仅覆盖当前 decoder 调用:decoder 必须立即读取所需字段并返回 Struct、Enum、
232
+ Option、Result 或其他 Calcit-owned data;调用方不能凭该 evidence 在稍后的代码中直接访问原
233
+ 对象。若 adapter 必须保留外部对象或在稍后调用其方法,每一次 field/method access 都要重新
234
+ 检查存在性和 primitive/function shape,或改为 debug 模式提供这类断言。external-object trait
235
+ 仍只提供静态成员声明与代码生成映射,不证明运行时对象不可变或始终符合该 shape。
236
+
237
+ unsafe 模式可以有意跳过这些检查,但必须把 `unsafe-coerce` 保留在最小 adapter 中,并由针对
238
+ 该 binding 的正向和负向 runtime contract test 提供可追溯证据。
239
+
240
+ ## 实施阶段
241
+
242
+ ### Phase 0:真实失败目录
243
+
244
+ - 从 js-ffi、Editor、Respo workflow 和网站项目收集“静态通过但 runtime 失败”的最小 fixture;
245
+ - 按 target/binding/value/callback/exception/runtime identity 分类;
246
+ - 为已有 public wrapper 标记 raw/host/normalized 层。
247
+
248
+ ### Phase 1:库级 guard 与 contract tests
249
+
250
+ - 统一 `JsError`、primitive/nullish/function guards 和 object decoder;
251
+ - Node contract test 覆盖 module import、process、exception、Promise;
252
+ - browser contract test 覆盖 DOM、event、receiver 和 storage;
253
+ - effect wrapper 收敛为 Unit 或 Result。
254
+
255
+ ### Phase 2:编译器 evidence 与诊断
256
+
257
+ - query/analyze 暴露 host contract evidence;
258
+ - unchecked coercion 进入 weak/quality report;
259
+ - target mismatch 在 codegen 前失败;
260
+ - runtime contract error 附带 Calcit definition/path。
261
+
262
+ ### Phase 3:生态采用
263
+
264
+ - js-ffi 自身达到 Q3;
265
+ - Editor、Respo workflow 和网站各选择至少一个真实 browser consumer 回归;
266
+ - 新 public FFI binding 必须同时提交 schema、contract test 和 normalized API 决策。
267
+
268
+ ## 验收标准
269
+
270
+ 1. 每类已知“类型通过但 runtime 失败”都有最小正向和负向 fixture。
271
+ 2. public js-ffi wrapper 不以无理由 Dynamic 隐藏 primitive/nullish/callback 关系。
272
+ 3. 从 host object 到 Struct/Enum 的路径经过 decoder 或被明确报告为 unsafe。
273
+ 4. Node/browser target mismatch 在运行前失败。
274
+ 5. module method 的 receiver lowering 有真实执行测试。
275
+ 6. throw/rejection/nullish 分别映射为 Result/Option,而不是偶然的 undefined/Dynamic。
276
+ 7. quality 能报告 unchecked host assertion,但不伪造 runtime-test 状态。
277
+ 8. 不使用 JS FFI 的程序在类型推断、trait matching 和 codegen 上不受影响。
278
+
279
+ ## 非目标
280
+
281
+ - 完整导入 TypeScript `.d.ts`;
282
+ - 通用 structural object/union/overload 类型;
283
+ - 运行时深度验证每一次宿主属性读取;
284
+ - 用 FFI 推动 generic trait、associated type 或 effect row;
285
+ - 保证任意第三方 JavaScript 包升级都不会破坏契约。
286
+
287
+ ## 相关资料
288
+
289
+ - `RFCs/07-08-ffi-features-and-js-object-type-rfc.md`
290
+ - `RFCs/07-31-unsafe-coerce-driven-static-type-boundary-plan.md`
291
+ - `RFCs/08-08-cross-backend-host-ffi-contracts-rfc.md`
292
+ - `RFCs/08-18-calcit-typed-js-ffi-boundary-rfc.md`
293
+ - `RFCs/08-21-type-quality-ci-adoption-rfc.md`
294
+ - `docs/features/js-interop.md`
295
+ - `docs/data/edn.md`
@@ -0,0 +1,241 @@
1
+ # RFC:setup-calcit 版本来源与基础工具链契约
2
+
3
+ 状态:Draft
4
+
5
+ 日期:2026-08-21
6
+
7
+ ## 摘要
8
+
9
+ 普通 Calcit 项目的编译器版本应只有一个项目级事实来源:`deps.cirru` 中的
10
+ `:calcit-version`。新的 `setup-calcit` 默认用法只负责读取这个版本并安装相应工具,不再鼓励在
11
+ workflow 中重复填写 `version`。
12
+
13
+ `version` input 暂不立即删除,因为 setup-calcit 自身测试、没有 `deps.cirru` 的临时任务和紧急
14
+ 诊断仍然需要显式版本;但它降级为 fallback。若项目同时提供两个不一致的版本,Action 必须
15
+ 明确失败,不能再用隐含优先级覆盖。
16
+
17
+ 在这个单一版本来源基础上,setup-calcit 可以补齐缓存、校验、平台路径、结构化输出和工具选择等
18
+ 基础能力,但仍保持“安装工具”的单一职责。依赖安装、类型门禁和项目测试继续由显式 CI step
19
+ 执行。
20
+
21
+ `calcit-lang/setup-cr` 保留为旧 workflow 的兼容入口。GitHub Actions 不会为 Action 仓库改名
22
+ 提供重定向,所以不直接重命名该仓库;新项目迁移到 setup-calcit,旧项目继续使用已发布的 tag。
23
+
24
+ ## 背景
25
+
26
+ 当前 setup-cr README 的主示例仍然展示:
27
+
28
+ ```yaml
29
+ - uses: calcit-lang/setup-cr@0.0.8
30
+ with:
31
+ version: "0.9.6"
32
+ ```
33
+
34
+ 文档又说 `deps.cirru` 中的版本优先,而当前实现实际先读 `deps.cirru`,随后用 `version` input
35
+ 覆盖。这产生了三个问题:
36
+
37
+ 1. 项目升级 `deps.cirru` 后可能忘记同步 workflow,CI 安装旧 CLI;
38
+ 2. 使用者无法从文档可靠判断冲突时采用哪个版本;
39
+ 3. 旧 CLI 可能重写新 Snapshot 的 schema 或 metadata,最终表现为格式差异、类型退化或陌生的
40
+ CI 失败,而不是清晰的版本冲突。
41
+
42
+ 2026-08 的 0.13.27 生态升级已实际出现这种失败:项目配置已经升级,workflow 中的固定版本仍
43
+ 停留在旧版,旧 formatter 随后改变了新 Snapshot。问题不在 formatter 是否“足够兼容”,而在
44
+ 项目同时保存了两个互相矛盾的编译器版本。
45
+
46
+ 当前实现还有一些与规模增长不匹配的基础限制:
47
+
48
+ - 用正则读取版本但没有检测多个或畸形声明;
49
+ - 下载任务没有统一 await,失败聚合和完成时机不够明确;
50
+ - 安装目录固定为 `/home/runner/bin`,把实现绑定到 Ubuntu 路径;
51
+ - 没有公开 resolved version/source/tool paths 等 outputs;
52
+ - 没有复用 tool cache,也没有下载完整性校验;
53
+ - 已不再需要的 `bundler` input 和 `bundle_calcit` 下载路径仍留在 Action 中;
54
+ - `cr-wasm` 等可选工具继续逐个增加布尔 input,扩展性较弱;
55
+ - Action runtime 版本和测试矩阵需要跟随 GitHub Actions runner 演进。
56
+
57
+ ## 决策一:`deps.cirru` 是正常项目的版本事实来源
58
+
59
+ 推荐文档示例改为:
60
+
61
+ ```yaml
62
+ - uses: actions/checkout@v4
63
+ - uses: calcit-lang/setup-calcit@v1
64
+ ```
65
+
66
+ 对应项目配置:
67
+
68
+ ```cirru.no-check
69
+ {} $ :calcit-version |0.13.27
70
+ ```
71
+
72
+ README、Calcit 安装文档、模块模板和 workflow 模板都应优先展示无 `version` input 的形式。
73
+ 显式 input 移到“无项目文件的任务与故障诊断”小节,不再作为 quick start。
74
+
75
+ ### 确定的解析规则
76
+
77
+ 1. 默认读取仓库根目录的 `deps.cirru`;
78
+ 2. 所选 `deps-file` 缺失、或文件中没有 `:calcit-version` 时,才允许使用显式 `version`;
79
+ 3. 出现多个声明或任一声明不是合法 SemVer 时,以 `E_SETUP_VERSION_INVALID` 失败,绝不回退
80
+ 到 `version` input;
81
+ 4. 找到且只找到一个合法 `:calcit-version` 时,以它为项目版本;
82
+ 5. 同时传入相同的 `version` 时允许执行,但 summary 标为 redundant;
83
+ 6. 两个合法来源不一致时以 `E_SETUP_VERSION_CONFLICT` 失败,并同时打印两个来源和值;
84
+ 7. 两者都不存在时失败,并给出创建 `deps.cirru` 的首选修复方式。
85
+
86
+ 这里不再定义“谁的优先级更高”。存在冲突就代表项目状态不自洽,应先修复项目,而不是猜测
87
+ 维护者意图。
88
+
89
+ ### 多目录项目
90
+
91
+ 增加可选的 `deps-file` input,默认值为 `deps.cirru`。它只用于 monorepo 或非根目录项目:
92
+
93
+ ```yaml
94
+ - uses: calcit-lang/setup-calcit@v1
95
+ with:
96
+ deps-file: examples/browser/deps.cirru
97
+ ```
98
+
99
+ 路径必须位于 checkout workspace 内。缺失的所选文件等同于没有项目声明:只能使用显式
100
+ `version`,没有该 input 时以 `E_SETUP_VERSION_MISSING` 失败;多个或畸形版本声明均以
101
+ `E_SETUP_VERSION_INVALID` 失败且不能回退。Action 不递归搜索“最像项目”的文件,避免在
102
+ monorepo 中安装偶然找到的版本。
103
+
104
+ ## 决策二:扩充基础能力,但不接管项目 CI
105
+
106
+ ### 工具选择
107
+
108
+ `calcit` 和 `caps` 保持默认安装。Calcit release 只发布 `calcit`,不再构建第二个 `cr` artifact。
109
+ Action 在安装目录创建相对的 `cr -> calcit` 链接,供旧 workflow 的 `run: cr ...` 继续使用;本地安装只提供
110
+ `calcit`。`tools` input 继续用可枚举形式取代不断增加的布尔项:
111
+
112
+ ```yaml
113
+ with:
114
+ tools: calcit,caps,cr-wasm
115
+ ```
116
+
117
+ 兼容输入中的 `cr` 被规范化为 `calcit`;同时请求二者属于重复项并在下载前失败。输出 `tools` 也只列出
118
+ 规范化名称,避免将兼容链接误当作第二个已下载工具。
119
+
120
+ `bundle_calcit` 已经不再需要,不进入 `tools`,对应 `bundler` input 和下载分支直接移除。
121
+ 兼容期只需继续接受 `cr-wasm` 布尔 input;转换后得到唯一的 requested tool set。未知工具、
122
+ 重复项或目标版本没有对应 artifact 时,在下载前失败。
123
+
124
+ setup-calcit 不增加 `run-tests`、`run-quality`、`install-modules` 等 input。以下步骤必须继续显式
125
+ 出现在 workflow 中:
126
+
127
+ ```bash
128
+ caps --ci
129
+ calcit calcit.cirru --check-only
130
+ calcit calcit.cirru analyze quality
131
+ ```
132
+
133
+ 这样 Action 升级不会悄悄改变项目测试范围,失败日志也能清楚区分安装、依赖、类型和运行测试。
134
+
135
+ ### 缓存和安装路径
136
+
137
+ - 使用 GitHub tool cache 按 `tool/version/platform/arch` 查找和保存工具;
138
+ - 临时下载使用 runner 提供的 temp 目录,不写死 `/home/runner`;
139
+ - 最终通过 `core.addPath` 暴露缓存目录;
140
+ - 所有下载 Promise 必须 await,任一失败后不报告安装成功;
141
+ - 相同 job 内重复调用时复用缓存并保持幂等。
142
+
143
+ 平台和架构必须进入 release artifact lookup。MVP 只声明并测试实际发布了二进制的组合;不把
144
+ Ubuntu 上的裸文件名假装成跨平台协议。
145
+
146
+ ### 完整性与版本自检
147
+
148
+ 短期至少在安装后执行轻量版本自检,确认 `calcit` 输出的版本与 resolved version 一致。中期由
149
+ Calcit release 发布机器可读 manifest,包含:
150
+
151
+ - release version;
152
+ - tool name;
153
+ - platform 与 architecture;
154
+ - asset name;
155
+ - SHA-256;
156
+ - 可选的 Snapshot/capability schema version。
157
+
158
+ Action 先验证 checksum,再加入 PATH。下载到了 HTML 错误页、旧缓存或命名错误的 artifact 时,
159
+ 应在 setup 阶段失败。
160
+
161
+ ### Outputs 与 Job Summary
162
+
163
+ 稳定 outputs:
164
+
165
+ | output | 含义 |
166
+ | --- | --- |
167
+ | `version` | 最终解析出的 Calcit 版本 |
168
+ | `version-source` | `deps-file` 或 `input` |
169
+ | `deps-file` | 实际读取的项目文件路径;无则为空 |
170
+ | `tools` | 成功安装的规范化工具列表 |
171
+ | `cache-hit` | 是否全部来自 tool cache |
172
+
173
+ Job Summary 只记录版本来源、工具、平台、缓存命中和自检结果,不输出 token、下载 header 或其他
174
+ 环境敏感信息。
175
+
176
+ ## 文档迁移
177
+
178
+ 1. setup-calcit README quick start 删除显式 `version`,并说明 setup-cr 的 legacy 兼容边界;
179
+ 2. Calcit README 将 `:calcit-version` 从“CI hint”提升为项目工具链版本;
180
+ 3. 新 workflow 模板统一引用 setup-calcit release,不复制 Calcit 版本;
181
+ 4. 显式版本用法放入 advanced usage,并说明不应与 `deps.cirru` 冲突;
182
+ 5. 错误文案修正历史拼写 `calcit-verison`,并给出实际读取路径。
183
+
184
+ Action 在 `@v1` 内保持兼容:优先下载 `calcit`,对旧 release 回退 `cr` 并暴露同名 `calcit` 命令;对新 release 则创建 `cr` 链接。安全要求更高的仓库仍可固定 commit SHA。
185
+
186
+ ## 实施阶段
187
+
188
+ ### Phase 0:文档与冲突检测
189
+
190
+ - README 默认示例改为从 `deps.cirru` 读取;
191
+ - 明确解析规则;
192
+ - 两个版本不一致时失败;
193
+ - 加入版本解析、默认或显式 `deps-file` 缺失、无声明、重复、畸形 SemVer、冲突的单元测试;
194
+ 缺失文件只在给出 input 时回退,重复和畸形声明均断言 `E_SETUP_VERSION_INVALID`,即使
195
+ workflow 同时给出 `version` input 也不能回退。
196
+
197
+ ### Phase 1:可靠安装
198
+
199
+ - await 全部下载;
200
+ - 使用 runner temp 和 tool cache;
201
+ - 增加 resolved outputs、summary 和安装后版本自检;
202
+ - 将 Action runtime 升到 GitHub 当前支持版本。
203
+
204
+ ### Phase 2:工具与平台矩阵
205
+
206
+ - 引入 `deps-file`、`tools`;
207
+ - 保持 `cr-wasm` input 的兼容转换与 deprecation 提示,移除已废弃的 `bundler`;
208
+ - 对实际支持的 OS/architecture 运行 self-test;
209
+ - 缺失 release artifact 时给出结构化错误。
210
+
211
+ ### Phase 3:release manifest
212
+
213
+ - Calcit release 产生 checksums/manifest;
214
+ - setup-calcit 校验下载完整性和 capability metadata;
215
+ - 缓存 key 纳入 manifest/schema version。
216
+
217
+ ## 验收标准
218
+
219
+ 1. 正常项目的 workflow 不填写 Calcit 版本,只改 `deps.cirru` 即可完成升级。
220
+ 2. `deps.cirru` 与 input 冲突时,Action 在下载前失败并展示两个来源。
221
+ 3. 安装日志能证明最终版本、来源、工具、平台和缓存状态。
222
+ 4. 任一工具下载或自检失败时 Action 必须失败,不能留下部分成功状态。
223
+ 5. setup-calcit 自身测试覆盖默认或显式缺失 deps、合法 deps、畸形 deps、重复声明、冲突和
224
+ artifact 缺失。
225
+ 6. Action 不隐式运行 `caps`、formatter、类型门禁或业务测试。
226
+ 7. 支持的平台都不依赖硬编码 `/home/runner` 路径。
227
+
228
+ ## 非目标
229
+
230
+ - 让 setup-calcit 成为新的包管理器;
231
+ - 自动修改 `deps.cirru`;
232
+ - 替项目选择“最新”版本;
233
+ - 隐式运行项目 CI;
234
+ - 通过兼容 formatter 掩盖 CLI/Snapshot 版本冲突。
235
+
236
+ ## 相关资料
237
+
238
+ - `README.md`
239
+ - `docs/run/library-quality.md`
240
+ - `RFCs/07-28-git-module-store-rfc.md`
241
+ - `RFCs/08-21-type-quality-ci-adoption-rfc.md`