feat: publish Dever core with project scaffolding and versioned AI skills
This commit is contained in:
1 parent
70fa73cdc2
commit
ac458ffa7f
960 files changed
+221319
-1163
No files matched your search
@@ -0,0 +1,3 @@
|
||||
{"file":".trellis/spec/backend/quality-guidelines.md","reason":"Focused architecture/effect/config tests"}
|
||||
{"file":".trellis/spec/backend/directory-structure.md","reason":"Single symbol/dispatch owner and legal role layout"}
|
||||
{"file":".trellis/spec/backend/error-handling.md","reason":"Port failure subset and diagnostics"}
|
||||
@@ -0,0 +1,47 @@
|
||||
# Dever Port 与 Adapter 设计
|
||||
|
||||
## Source Contract
|
||||
|
||||
Port role按路径给出边界身份。根 `port.dever` 的 identity 为 `<component>.<domain>`;topic Port 增加 topic。bodyless contract 示例:
|
||||
|
||||
```dever
|
||||
send(message: Text) () fails app.DeliveryError
|
||||
```
|
||||
|
||||
`fails` 必须引用同领域 App 的公开 error choice(例:App 定义 `DeliveryError`),或公开标准 error choice;至少有一个显式 error variant。Port 自有类型仍私有。这样保留 App 全部函数公开的规则,App 能传播自己的错误,无需增加隐式私有 helper。Port/Adapter 只能引用 Port 签名使用的 App 类型,不能调用 App。普通有 body function 仍沿调用图推断 failure,不新增通用 throws 声明。
|
||||
|
||||
Adapter/Test 通过 qualified definition 实现,例如 `port.send(...) () { ... }` 或 `port.mail.send(...) () { ... }`。普通单名 function 是该文件 helper。一个生产 Adapter 文件完整实现一个 Port identity;禁止跨 Port 聚合。一个 TestCase 可以完整 fake 多个 Port。
|
||||
|
||||
## Registration And Dispatch
|
||||
|
||||
Checker 在所有签名完成后建立 Port contract/implementation table。实现身份来自 adapter role topic;根 `adapter.dever` 使用 `default`。逻辑 Port identity 不包含 Adapter 文件路径。每个可达 Port 必须至少有一个生产实现;不可达声明按既有 unused contract 报 warning。
|
||||
|
||||
单实现 native call 直接静态调用。多实现生成固定选择和 concrete match,分支调用具体 specialization。effect analysis 在 HIR 中看到生产实现保守并集;failure 摘要固定为声明全集,实现实际失败必须是子集。运行时选择不改变类型正确性。
|
||||
|
||||
## Settings
|
||||
|
||||
Adapter 文件可声明一个 contextual `setting` record,并以只读 `setting.<field>` 访问。编译器把它纳入共享 setting wire schema。建议 JSON:
|
||||
|
||||
```json
|
||||
{
|
||||
"adapter": {
|
||||
"notification.mail": {
|
||||
"use": "smtp",
|
||||
"setting": {
|
||||
"endpoint": "https://mail.example.test",
|
||||
"token": "secret-value"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`use` 在多实现时必需,单实现可省略但若提供必须匹配。端点/证书/凭据属于 Adapter setting;重试业务规则和权限不放配置。错误只报告 Port/Adapter/字段位置,不回显 Secret 值。
|
||||
|
||||
## Test Fakes
|
||||
|
||||
Test qualified implementation使用相同 signature/failure checker,但 owner 是 TestCase。Suite emission 为每个 case 生成独立 closed binding;case selection 同时选择 test entry 和 fake table。没有 fake 的可达 Port 在测试检查阶段失败,不退回生产 Adapter,确保测试不会意外访问外部系统。
|
||||
|
||||
## Role Matrix Migration
|
||||
|
||||
`check/symbols.rs` 继续作为唯一 call/type access owner。新增明确矩阵而不是负向条件组合。Model operation owner 检查保持独立但与 role matrix 一致。现有 CMS 尚未使用 Adapter,迁移只需修复拒绝 fixtures 和文档,不保留 App-to-Adapter compatibility。
|
||||
@@ -0,0 +1,3 @@
|
||||
{"file":".trellis/spec/backend/compiler-contracts.md","reason":"Source role, visibility, failure and effect contracts"}
|
||||
{"file":".trellis/spec/backend/toolchain-and-library.md","reason":"Native dispatch, setting and official package boundary"}
|
||||
{"file":".trellis/spec/backend/database-guidelines.md","reason":"Transaction effect propagation and settings source"}
|
||||
@@ -0,0 +1,68 @@
|
||||
# Dever Port 与 Adapter实施计划
|
||||
|
||||
## Start Gate
|
||||
|
||||
- [x] secure-data-contracts 和 time-typed-codec 已完成。
|
||||
- [x] 用户批准父任务规划。
|
||||
- [x] 读取 source layout、parser declarations、symbol access、effects、application testing 和 config owners。
|
||||
|
||||
## Implementation
|
||||
|
||||
- [x] 增加 Port bodyless contract、fails modifier、AST/formatter/Markdown contract。
|
||||
- [x] 注册 Port types/contracts 和 Adapter/Test qualified implementations。
|
||||
- [x] 集中实现 role call/type matrix,删除宽泛 private-role互调分支。
|
||||
- [x] 检查 implementation signature、outputs、failures、effects 和覆盖完整性。
|
||||
- [x] 生成单实现直接调用和多实现 closed dispatch。
|
||||
- [x] 增加 Adapter setting declaration、共享 codec 接入和严格 setting.json binding。
|
||||
- [x] 把 Port effect 纳入 specialization/transaction/concurrency 检查。
|
||||
- [x] 扩展 TestCase fake binding 和 suite case隔离。
|
||||
- [x] 更新 LANGUAGE、Markdown、compiler/toolchain/application-testing specs。
|
||||
|
||||
## Implementation evidence (2026-09-20)
|
||||
|
||||
- 保留 Port 的 Function ID,附加生产实现 successors;shared dependencies/effects/specialization 纳入这些边,failure 直接 seed 声明全集。新 `check/ports.rs` 拥有声明、完整实现、setting schema 和 fake 绑定验证;`native/ports.rs` 生成直接调用/closed match 与不可变 setting/binding。
|
||||
- 合同修正:App 只有公开函数且语言用函数分句匹配,故“Port 私有失败再由 App 私有 helper 映射”不可表达。采用主代理确定的 `fails app.ErrorChoice`;App/Port/Adapter 共享已公开错误合同,禁止反向业务调用,不放宽私有类型导出。
|
||||
- 每个测试进程选定 case fake;check 与套件生成共用逐 case 的闭合图,Port successors 只替换为该 case 的 fake,生产 Adapter/配置不可达。每个 case 独立原生模块与同步/挂起入口,仍只编译一次套件。一个 case 可完整 fake 多个 Port;缺 fake 编译拒绝。
|
||||
- 新反例发现 Port 无 HIR body 导致原有挂起阻塞检查漏过合成 dispatch;已在同一 shared effects owner 校验 Port successors,拒绝挂起 Port 内联选择阻塞实现。
|
||||
- 当前已通过:`port_adapter` 10/10(单实现无配置、多实现同二进制切换、Secret setting、错误配置/重复字段、Markdown/fails、角色与 effects、逐 case fake 隔离);`source_architecture` 8/8;`source_visibility` 10/10;`application_config` 3/3;`markdown_source` 20/20。
|
||||
- 最终 `core/runtime/CLI` feature check(reference + api + postgres)通过;`application_testing` 8/8;`error_effects::result_capture_requires_matching_success_and_error_variants` 1/1;`git diff --check` 通过。
|
||||
- `structured_concurrency` 3/11 通过、8 个旧 fixture 失败:仍把 time/task.sleep 的 Unit 当 SleepResult、chunks 当 ReadStreamResult、直接构造 error variant,以及 Markdown H1 放代码。失败先发生在这些旧协议校验,未扩到无关 fixture 迁移;新 Port 挂起/阻塞定向反例已通过。
|
||||
- 未运行全量、外部服务、真实数据库或性能测试;rustfmt/clippy 组件未安装。Port 当前明确拒绝 handler 参数和输入/输出范围 bounds;配置选择/setting 仅原生后端执行,reference 对单个无 setting 实现可执行,其余给显式不支持错误。
|
||||
- 已完成清理自审:声明注册与实现注册分开;role I/O 验证收敛到 shared effects;配置可执行目录定位复用同一函数;Port/setting 合成函数不参与重复实现建议。任务状态留给主会话复核后推进,未提交或归档。
|
||||
|
||||
## Independent review (2026-09-20)
|
||||
|
||||
已修复四项实际问题:
|
||||
|
||||
- 测试 fake 曾在 native 阶段跨 case 合并;一个阻塞、一个挂起 fake 会触发错误的 Port 阻塞诊断。生产合同推断也曾使用 Test body。现在生产推断排除 Test body,check/native 共用 `ports::test_program` 的 case 闭合图;共享 dependency owner 保留未实例化 helper 的声明检查,逐 case 校验所有 fake operation 的 failure 子集。原生套件按 case 生成独立模块与同步/异步入口,仍一次 rustc、一个二进制。混合 fake 回归先失败、修复后通过;同 case 阻塞违规、未调用 fake 的越界 failure、未实例化 pure helper 的副作用均被拒绝。
|
||||
- `setting()` 普通函数曾被 parser 误判为 setting record;现在只在 `setting {` 时识别 contextual 声明。
|
||||
- 显式 `adapter: null` 曾被 serde Option 吞成缺省,单实现可意外启动;保留原始 null 交给 shared wire validator 拒绝,单实现无文件仍可运行。
|
||||
- Port-local DTO 内嵌 App DTO 曾在合同图展开之前触发 C006。现在在 shape access check 前按既有名字解析规则展开合同类型图;合法嵌套 DTO 通过,无关 App 类型仍被拒绝。正例已先复现失败再通过。
|
||||
|
||||
最终验证:`port_adapter` **10/10**、`application_testing` **8/8**、`source_visibility` **10/10**;core/runtime/CLI `cargo check`(reference + api + postgres)通过。命令使用 stable PATH、CARGO_INCREMENTAL=0、locked/offline、dev/test debug=0、serial tests。`git diff --check` 与本轮修改文件逐文件 `git diff --no-index --check /dev/null` 均无空白错误(后者有文件差异时退出 1,不代表空白错误)。LANGUAGE 与 compiler-contracts 已同步。
|
||||
|
||||
rustfmt/clippy 未安装,未运行且未安装组件。未运行全量测试、服务、真实数据库或性能测试。此前 structured_concurrency 的旧 fixture 失败记录仍适用,不在本轮扩改。无新增未修复的 in-scope 阻塞;未提交、未归档、未更改任务状态。
|
||||
|
||||
## Focused Verification
|
||||
|
||||
```bash
|
||||
cargo check --offline -p dever-core -p dever-runtime -p dever-cli
|
||||
cargo test --offline -p dever-tests --test source_architecture
|
||||
cargo test --offline -p dever-tests --test source_visibility
|
||||
cargo test --offline -p dever-tests --test error_effects
|
||||
cargo test --offline -p dever-tests --test structured_concurrency
|
||||
cargo test --offline -p dever-tests --test application_config
|
||||
cargo test --offline -p dever-tests --test application_testing
|
||||
cargo test --offline -p dever-tests --test markdown_source
|
||||
git diff --check
|
||||
```
|
||||
|
||||
不运行外部网络、全量测试、真实数据库或性能基准。Adapter runtime 正例使用 test-owned本地确定性实现,不连接服务。
|
||||
|
||||
## Review Risks
|
||||
|
||||
- Port implementation不能被普通 name lookup 当成可调用函数。
|
||||
- 多实现 effect/failure union不能因当前 setting 选择而缩窄静态合同。
|
||||
- setting schema identity必须包含实现选择,且错误不得回显 Secret。
|
||||
- 单 suite 的 fake registry不能跨 TestCase共享可变状态或生产绑定。
|
||||
- role matrix应由一个表/函数拥有,避免 call与type access规则漂移。
|
||||
@@ -0,0 +1,42 @@
|
||||
# Dever Port 与 Adapter
|
||||
|
||||
## Goal
|
||||
|
||||
把外部 HTTP、邮件、对象存储等依赖变成编译期验证、运行时可配置替换的 Port/Adapter 边界,同时收紧应用角色依赖,禁止 App 绕过 Port 和 Domain/Adapter 反向依赖业务层。
|
||||
|
||||
## Dependencies
|
||||
|
||||
- `secure-data-contracts`:Secret setting 和不可观察边界。
|
||||
- `time-typed-codec`:类型化 setting 解码和共享 wire schema。
|
||||
|
||||
## Requirements
|
||||
|
||||
- `port.dever`/`port/*.dever` 只允许类型和 bodyless function contract;每个 contract 显式 `fails` 同领域 App 公开 error choice 或公开标准 error choice,至少一个显式 error variant。Port 自有 DTO 仍私有。
|
||||
- Port contract 输入、命名输出、failure contract 和逻辑身份稳定,不从任一 Adapter 实现反推。
|
||||
- Adapter 通过 qualified Port target 显式实现;签名必须相同,实际 failure set 必须是声明集合的子集。
|
||||
- Adapter 可包含同文件私有类型/helper,但不能调用 App、Domain、Model、其他 Adapter 或 Port;只能调用标准库和自身 helper。
|
||||
- App 只能调用同领域 Port;Domain 只能调用同领域 Domain;Model 无业务函数;API/Job 只调用同领域 App。
|
||||
- Port 调用的 suspend/effect 是所有可选实现的保守并集,仍由现有静态 effect 系统决定 worker/await 边界。
|
||||
- 单实现自动绑定;多个实现必须由 `config/setting.json` 的稳定 Port identity 选择,未配置、未知实现或未知 setting 字段启动失败。
|
||||
- Adapter 可声明一个 typed setting record;只有被选择实现的 setting 必须存在并解码,Secret 字段不进入日志/错误。
|
||||
- 编译产物包含闭合实现集合和直接 match,不使用动态库、反射、trait-object business dispatch 或运行时源码扫描。
|
||||
- Test 可在同一个测试文件完整 fake 多个 Port;fake 只绑定当前 TestCase,测试不读取项目 Adapter setting 或回退到生产实现。
|
||||
- Port/Adapter 的 `.dever.md` 文档合同与普通源码一致。
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [x] 合法 bodyless Port 和单 Adapter 可被 App 调用并生成直接静态实现调用。
|
||||
- [x] Port body、Adapter 缺失/重复实现、签名/命名输出/failure 不匹配在 check 阶段拒绝。
|
||||
- [x] App 直调 Adapter、Domain 访问 Model/Port/Adapter、Adapter 反调 App/Domain/Model 和跨领域 Port 调用全部拒绝。
|
||||
- [x] 多 Adapter 通过 `setting.json` 在不重新编译的情况下选择;未知选择、缺配置、未知字段和错误类型启动失败。
|
||||
- [x] 未选择 Adapter 的 setting 不要求存在,其代码仍通过静态检查并包含在闭合产物中。
|
||||
- [x] Secret setting 可以用于批准 sink,但不能出现在日志、错误、API/Job output 或普通字符串中。
|
||||
- [x] 两个应用测试可以对同一 Port 使用不同 fake,单套件编译后各自运行且互不泄漏。
|
||||
- [x] Port effect/failure 通过 App、transaction、handler specialization 正确传播,不出现动态 fallback。
|
||||
|
||||
## Out Of Scope
|
||||
|
||||
- 运行时安装插件、动态库、服务发现和远端代码加载。
|
||||
- 跨领域直接共享 Port;领域间继续通过 App 合同协作。
|
||||
- 通用依赖注入容器、Service locator 或应用可变全局 registry。
|
||||
- Adapter 热切换;setting 在进程启动时验证并固定。
|
||||
@@ -0,0 +1,138 @@
|
||||
# Research: Port / Adapter 最小落地路径
|
||||
|
||||
- Query: 按现有编译器、静态合同、共享 wire、应用测试机制实现 bodyless Port、闭合 Adapter 选择和 case-local fake。
|
||||
- Scope: internal
|
||||
- Date: 2026-09-20
|
||||
- Task: `/data/project/dever/.trellis/tasks/09-20-port-adapter`,来自父代理显式分派;本代理 `task.py current --source` 返回 none,未修改 task 状态。
|
||||
|
||||
## Findings
|
||||
|
||||
### 1. 语法与声明注册
|
||||
|
||||
现状:`syntax.rs:168` FunctionClause 的 name 是单个 Name,body 为 Vec;`FunctionKind` 只有 Ordinary/Transaction。`parser.rs:685` 强制单名和 `{}`,不存在可复用 bodyless 声明;`function_contracts:718` 已将 pure/recover 作为 contextual Name 处理,可复用 `at_name`,无需新增全局 token 关键字。Parser 已拥有 source layout,但结构只保留 model/application,可增加 role 或必要布尔上下文。
|
||||
|
||||
建议最小 AST:保留普通 name,不将所有函数名改 Path;加 `implementation: Option<Path>`、`failure: Option<TypeRef>` 与明确 bodyless 标记(FunctionKind::Port 或独立 declaration kind 均可,不能用空 Vec 判断,因为普通空函数合法)。qualified definition 的末段仍为 name,前缀保存 Port target。仅 Port role 接受 bodyless/fails;仅 Adapter/Test 接受以 port 开始的 qualified definition。普通函数继续当前推断合同,遇到 fails 直接拒绝。Port 仅一个无模式、无 handler 参数的签名起步,拒绝 transaction/recover、重载歧义和多 clause;若要支持 handler 合同,必须额外定义实现方 failure/effect 参数依赖,不能默默沿用未闭合语义。
|
||||
|
||||
推荐语法(其中 `setting` 是新 contextual record 声明,不是 `type setting`):
|
||||
|
||||
```dever
|
||||
// module/notification/mail/port.dever
|
||||
type DeliveryError {
|
||||
error Unavailable(message: Text)
|
||||
error Rejected(message: Text)
|
||||
}
|
||||
send(message: Text) () fails DeliveryError
|
||||
|
||||
// module/notification/mail/adapter/smtp.dever
|
||||
setting {
|
||||
endpoint: Text
|
||||
token: Secret
|
||||
}
|
||||
port.send(message: Text) () {
|
||||
deliver(message, setting.endpoint, setting.token)
|
||||
}
|
||||
// deliver 为同文件 helper;真实外部失败需捕获并映射到 port.DeliveryError
|
||||
|
||||
// test/notification/mail/delivery.dever
|
||||
port.send(message: Text) () {
|
||||
assert_eq(message, "hello")
|
||||
}
|
||||
delivery() () {
|
||||
app.deliver("hello")
|
||||
}
|
||||
```
|
||||
|
||||
注意:目前 role 目录至少两文件,不能以只含 smtp.dever 的 adapter/ 作为合法单实现例子。单实现使用 adapter.dever(identity default),多个实现再用 adapter/smtp.dever 与 adapter/local.dever。
|
||||
|
||||
`check.rs:97-177` 按 qualified name + arity 合并 clauses;qualified 实现必须有自己的物理内部函数 ID,另存 target contract ID,不能注册为原 Port 名或让两个 Adapter 合并成 clauses。`check.rs:236` 先完成全部签名,适合在此后建立 contract/implementation table;bodyless 跳过 clauses coverage/body 输出赋值检查。`check/names.rs:60` 对 qualified 各段独立 snake 检查。`check.rs:485` 现有 Port 一律拒绝应替换成正向声明校验。
|
||||
|
||||
### 2. 路径、调用与类型权限
|
||||
|
||||
路径已有足够信息:`source.rs:25-105` SourceRole 含 Port/Adapter,Role 保存 component/domain/topics,Test 保存 component/domain/topic。`check/layout.rs` 负责 role 文件/目录互斥、flat topics、至少两文件;不需新目录体系。
|
||||
|
||||
`check/symbols.rs:206` can_call 当前:App 能调全部同域 private role;Domain/Model/Port/Adapter 可相互调同域非 App;Test 只能同域 Domain / 任意 App / 自身 Test;API 完全不可作为目标。需要集中 role policy,至少区分 Call 与 Type,不能用同一布尔强行覆盖所有访问。
|
||||
|
||||
建议调用矩阵:Main→App;API→本域 App;App→任意 App、本域 Domain/Port;Domain→本域 Domain;Adapter→同文件普通 helper;Test→同文件 helper、本域 Domain、任意 App。Port contract 无 body。生产 qualified implementation 永远不能被 source lookup/handler capture 直接引用。Adapter 不能调其他 Adapter 的 helper,即使同域也不行。Model 操作仍由 `check/orm.rs:247-258` 独立限定本域 App,不能只靠 function call 矩阵,因为 ORM bypass 普通函数 lookup。
|
||||
|
||||
`can_access_type:256` 目前先放行 public Model type、自己文件,然后同域 Domain/Port/Adapter 类型互通;因此“Domain 不访问 Model”“Adapter 不反向依赖业务层”必须同时收紧类型 owner,而不是只收紧调用。建议 Adapter 可引用自身类型、本域所实现 Port 的合同类型、public 标准类型;Port 可引用本 Port 类型/public 标准类型,避免通过 App/Domain 造成反向依赖;Domain 可引用本域 Domain 与允许的 App DTO(若保留现有 business error 约定),但不可访问 Model/Port/Adapter。App 可引用本域 Domain/Port、公开 App/Model,不能引用 Adapter setting/helper type。Test 为 fake 新增本域 Port 类型访问,但不新增直接 Port 调用权限。类型矩阵是否允许 Domain 引用 App DTO 与“Domain 只调用 Domain”并不冲突,需明确不要顺手删除现有 App 错误类型引用约定。
|
||||
|
||||
重要合同矛盾:`check.rs:559` 拒绝 App 传播 private error 类型。Port 内定义的 DeliveryError 是 private,不能直接从 App 泄漏。最小解法是 App 用既有 result capture + error-only choice wrapper 捕获,再映射 App 自己的公开 error;不要将 Port 类型整体 public 或放开跨域 Port。Port 返回私有 DTO 同理在 App 映射 View。
|
||||
|
||||
另一个绕过:can_call 在 `target.bundled` 提前放行所有标准公开函数。单纯角色矩阵不能阻止 App/Domain 直接调用 HTTP/socket/process/file,也不能阻止 Adapter 调用 dever.api.serve。若目标包括“不绕过 Port 外部 I/O”,必须添加 capability 校验并覆盖 direct system intrinsics、别名、handler 与间接调用。现有 effects 是 time/network/blocking 等粗粒度,不能只禁止全部 network(API serve 需要);应按具体允许的边界/effect owner 定义规则,不从模块名 substring 猜。需要父代理固定本期准确范围。
|
||||
|
||||
### 3. HIR / failure / effect / specialization
|
||||
|
||||
`hir.rs:11` Program 存摘要向量,`Function:71` 存普通函数签名与 clauses,`CallTarget:513` 仅 Function/Handler。最低侵入路径:Port 仍拥有一个 Function ID,新增 Program.port_contracts / adapter_implementations;调用保留 CallTarget::Function(port_function),native 对该函数发出 compiler-owned dispatcher。这样签名解析、result、run/blocking、handler reference 继续用一个 ID,不必对所有 CallTarget match 添加 Port 分支。但必须让 contract Function 明确无 body,并在每个分析 owner 注入 Port 摘要和实现边,不能仅生成 native wrapper。
|
||||
|
||||
推荐数据:PortContract { function, identity, allowed_failures: BTreeSet<Failure>, implementations: Vec<ImplementationId> };AdapterImplementation { owner, name, port, operations: BTreeMap<contract_function, implementation_function>, setting_type: Option<TypeId> };TestCase.fake_bindings: BTreeMap<port_id, implementation_id>。identity 根为 component.domain、topic 为 component.domain.topic;实现 name 为 adapter topic 或 default,不使用 package 路径、源码 ID、排序下标作为 JSON 稳定身份。不同 operation 同名/同 arity 用完整 Port identity 区分。
|
||||
|
||||
`contracts/dependencies.rs:11` 是共享依赖 owner:contract→所有生产实现;effects/failure worklist reverse callers 必须看到这些边。`specialize.rs:174` Frame::new 当前只遍历 expression static calls,应扩展 synthetic Port successors;`concrete()` 会枚举所有普通函数,也要避免把 fake 当全局生产实现。native `expressions.rs:310` 统一 call_instance/invoke 可复用;dispatcher 每个分支直接调用具体 specialization,并按分支实际 suspension await。所有候选实现必须参与 reachable/codegen,不能按 build-time setting 剪枝,否则无法不重编译切换且 runtime features 错漏。
|
||||
|
||||
failure 不是简单 union:`contracts/error_effects.rs:42` 每轮从空摘要推断;Port 函数需每轮 seed **声明允许集合**,而非实现实际 union。否则一个实现暂未抛出的声明错误会消失,`result(call)` 精确错误集合验证随实现变化,违反合同稳定性。允许集从指定 Shape::Choice 中 `variant.error == true` 变体映射 `Failure {ty,variant}`;不得按名字或消息匹配。实现实际 program.failures 是允许集子集,且必须无未约束 handler error 集。实现不写 fails;实现 errors 超集在 contracts 完成后报错。凡 choice 非 error variant 不属于允许集;建议要求至少一个 error variant。是否允许 error-free Port 需明确无失败语法,当前 PRD “每个显式 choice”没有表达空集合的办法,不能假造 error。
|
||||
|
||||
`contracts/effects.rs:228` Effects 摘要要 union 所有候选的 known/recoveries/databases/blocking/suspends/handler dependencies;Port 无 body时目前会推成空。`contracts/failures.rs:571` 为值内 failure obligation,不等于 error_effects 的传播错误,不能混淆。`contracts/facts` / clause bounds 如果 Port 带输出 bounds,需要使用已声明输出保证且严格校验实现,不能因空 body绕过保证。最低范围可拒绝 Port bounds,文档明确,而非静默忽略。
|
||||
|
||||
### 4. config 与 Secret setting
|
||||
|
||||
`runtime/config.rs:72` Document deny_unknown_fields,目前仅 app/http/database/log;app/log 被丢弃,http 延迟解析。加入 adapter 顶层必须修改此唯一 owner。`Settings::load:138` 使用 serde_json::from_str,map 会丢失重复 key;不要先 from_value 再 wire decode,因为重复 key 已不可恢复。建议 adapter 部分保留 raw JSON 子树或用既有 runtime wire Node 从原文本定位,再交共享 concrete decoder;对 Port key、use、setting 重复/未知字段都严格检查。以 bounded wire parse 先验证整个文档也可,但要确认既有配置大小/深度合同变化。
|
||||
|
||||
`wire.rs:7` Policy::SettingInput 已允许 Secret;`Schema::build/from_field` 是 eligibility owner,支持 visible record,不允许 bounded/private fields、Choice/Map/ModelId/Related。`native/wire.rs:7` emit 已产生 `<prefix>_decode(text)` 返回具体 T{id},SettingInput 不产生 encoder;Secret 使用 from_input(text bytes),无 need 自制 JSON 转换。register setting 为 owner-private 合成 record(如物理 Adapter 下 Settings),字段本身不可 private,schema fingerprint 包含 policy/nominal identity。setting 字段读取应新 HIR SettingField 或 Setting record expression;禁止赋值、局部 shadow 绕过、从 Adapter 输出 Secret。无需暴露 generic runtime Value 给业务。
|
||||
|
||||
JSON 建议仍使用设计稿 `{ "adapter": { "notification.mail": { "use":"smtp", "setting":{...} } } }`:它表达一个 Port identity 选一个实现,该实现完整实现此 Port 的所有 operations。一份 Adapter 实现多个 Port 时,setting 生命周期/多份配置 ownership 尚不一致,MVP 应禁止并以明确诊断说明,或先规定每 Port 独立配置,不能暗中跨 Port 复用某一个 setting。
|
||||
|
||||
单实现无 setting 时可完全省略 adapter 条目;单实现有 setting 时条目/setting 必需,use 可省略;多实现必需 use;若配置 use 必须匹配。未选实现 setting 不解码,但所有候选源码/Schema 要检查。未知 Port identity 需按编译闭合表拒绝。选中实现无 setting 时若提供非空 setting 应拒绝(建议完全不允许 setting key,规则更清晰)。错误上下文只报 Port/Adapter/字段路径,不回显原值。
|
||||
|
||||
`config::bootstrap:272` 当前将 settings/数据库初始化与 OnceLock 绑定;`native.rs:96` 仅有 profile 才调用 database setup。Port-only 应用也要初始化选择,不能搭便车要求 database 或 HTTP。建议从数据库 setup 抽取共享 settings 初始化/读访问,再生成 Adapter 初始化,保持只读固定绑定;按全部 required bindings 校验完再执行用户 main 或任何外部 I/O。generated immutable OnceLock<closed enum with concrete settings> 符合“一次启动固定”且不是可变 service registry。Secret 不可 Debug/render、不要 derivation泄漏。
|
||||
|
||||
`native/build.rs:346` runtime feature selection 与 generated code关联;所有候选 emission保证 HTTP/crypto特征不丢。`native/build/cache.rs:163` cache 包含生成源码和 runtime 输入;选择值不应进入生成源码,metadata/schema/候选集合必须进入。CLI run/build 会预读部署 Settings,须允许新 adapter section且 build不把 use 静态固化;测试运行时不读取部署 Settings。
|
||||
|
||||
### 5. case-local fake
|
||||
|
||||
`hir.rs:28` TestCase 只有 checked function/span/database metadata;`check.rs` finalize_tests 在 contracts 后;适合加入 fake table 以及每 case Port reachable 验证。相同测试文件就是一个 entry/TestCase,qualified fake 私有实现归属该 owner。fake 与生产实现都走同一个签名/allowed failure checker,fake 不加入生产 adapter table;无 fake的可达 Port check 失败,即使生产恰好单实现也不能退回。
|
||||
|
||||
`native.rs:142` suite 单次 emit_definitions 全部 test roots,selected index选择 case;`test_runner.rs:70` 每 case 独立进程/临时项目,仅数据库 case写隔离SQLite setting,现成强隔离可复用。
|
||||
|
||||
建议使 specialization 带 immutable binding context(Production / Test(case id)),并在传播 App/helper/handler调用时保持此上下文,Port 在 Test context仅追加对应 fake successor。同一个 App 被两个 case调用将生成两个必要 specialization,不会共享错误 fake。alternative 是每进程选定 immutable fake dispatch enum,但 suite静态 effect/reachability必须仍排除生产实现,并处理同 App不同 fake suspension;该路线较易误把生产代码/features带入 suite。切忌用可变全局 fake registry且每 case reset。
|
||||
|
||||
静态生产合同仍是生产实现 effect union,fake不得让业务合同缩窄;测试 codegen可使用各 fake实际 suspension决定 await,但函数签名/specialization应一致,必要时保守 async化。finalize_tests 的 database metadata 要基于 Test context可达集合,防止从生产Adapter推导数据库(理论上Adapter禁止Model操作,但其他effects同理)。测试缺fake检查应检查经App、普通helper、静态handler、run/blocking所有到达路径,不能只扫Test直接calls。
|
||||
|
||||
### 6. Markdown / formatter
|
||||
|
||||
`format.rs:264-309` 从token重新组装函数名、pure/recover、body;需要输出qualified path、fails类型、bodyless无花括号,保留注释消费顺序。setting 新 Declaration 分支须覆盖 formatter、markdown::declaration_span、source section validation、names及所有 Declaration exhaustive matches。
|
||||
|
||||
`markdown/contract/model.rs:36/60` 文档函数仅name/inputs/outputs;`metadata.rs:52` strict ordered元数据,建议Port段新增 `- 允许失败:` 只在Port存在,与 AST failure choice精确比较;Adapter段 name 使用完整 `port.send`,无额外猜测。setting段作为独立record-like Section,字段类型/顺序/数量与源码一致。`validate.rs:232` 用 owner + 最后一段函数名 + arity寻找HIR,会错误定位qualified实现,改为AST declaration span或显式 declaration↔HIR ID映射,不能 `.rsplit('.').next()` 猜。public_functions metadata继续角色导出规则,不能把实现列为公开App能力。
|
||||
|
||||
### 7. 建议分阶段实现与最小定向测试
|
||||
|
||||
1. AST/parser/formatter/Markdown + contract/implementation registration + role矩阵;只检查,不先运行native。正反例包括Port body、普通fails、qualified非法owner、缺/重复operation、参数类型/输入名/输出名/顺序/arity不匹配、跨域target。
|
||||
2. 固定failure declaration seed、effect union、synthetic dependencies、handler/run/transaction分析;测试一个blocking实现+一个suspending实现的保守拒绝、错误子集/超集、捕获声明全集,生产配置不能改变合同。
|
||||
3. shared wire setting + strictJSON选择 +直接native dispatch;确定性本地实现两分支,不联网;同一二进制仅换配置得到不同结果、未知use、缺use、未知字段、重复key、错误类型、未选配置不需存在、选中Secret错误不回显。
|
||||
4. TestCase绑定与specialization context;两个测试同一个App各自fake结果、同套件单编译、无fake失败、生产配置坏/含真实地址仍不读取、生产实现不作为fallback、case执行失败不影响后续。
|
||||
5. 同步LANGUAGE/Markdown/compiler/toolchain与root test fixtures,独立复核。
|
||||
|
||||
现有入口:`test/dever-tests/tests/{source_architecture,source_visibility,error_effects,structured_concurrency,application_config,application_testing,markdown_source,formatter}.rs`。建议新 `port_adapter.rs` 聚合端到端闭合合同;只运行必要exact测试和cargo check,无真实HTTP/数据库/全量。旧source_architecture中“port reserved”和App→Adapter正例要按新合同更新,不能保留兼容开关。
|
||||
|
||||
## Related specs
|
||||
|
||||
### 补充:建议本期固定的执行决定
|
||||
|
||||
Port failure 合同建议本期采用声明全集、实现子集、App显式映射三条规则。普通App不透传Port私有错误;`fails DeliveryError` 只提取error variants,result仍要求全集匹配。先要求每个Port都有至少一个error variant,error-free语法另行设计,避免本期引入第二种fails语义。
|
||||
|
||||
“App不能绕过Port”必须本期实施,否则PRD目标未满足。最小可执行限制是:App/Domain禁止未经Port边界的 `file` 和 `network` intrinsic可达路径;Domain的Model操作另由既有owner检查拒绝。复用 `contracts/effects.rs:547` 的穷尽intrinsic_effect分类,不另写模块名白名单。新增一个内部摘要 `unmediated_io`,沿普通函数和具体handler传递,但穿过Port contract时清空这一项;正常effect/blocking/suspension摘要仍完整union,不能一并清空。这能允许App→Port→Adapter→HTTP,并拒绝App→helper/标准包装→HTTP。检查direct intrinsic同样经过此摘要,handler绑定用既有specialization保守展开。Adapter只允许自己的helpers/标准库,仍需单独禁止 `ApiServe`,并将该入口限制为Main显式启动;不要把整个network类禁在Adapter上。日志、时间、随机、密码散列、process.arguments本期保持已有授权,不把“Port约束”扩大为全新纯函数制度。若希望Domain完全无任何副作用,属于更强约束,不能偷偷从本期文件/网络隔离推出。
|
||||
|
||||
- `.trellis/workflow.md`:研究仅写task research;main负责后续 implement/check。
|
||||
- `.trellis/spec/backend/compiler-contracts.md`:source角色、App公共边界、failure/effects/shared dependencies、Model owner、Test权限、wire sole owner。
|
||||
- `.trellis/spec/backend/toolchain-and-library.md`:Markdown严格合同、native cache/feature选择、配置只用setting.json、Secret不可观察。
|
||||
- `.trellis/spec/backend/directory-structure.md`:目录/role归属。
|
||||
|
||||
## External references
|
||||
|
||||
本研究仅依据当前仓库,不引入新依赖,不需要外部版本或API资料。
|
||||
|
||||
## Caveats / Not Found
|
||||
|
||||
- 当前没有 bodyless contract、qualified实现、setting record、Port dispatch 或 fake机制;均不能靠修改一条调用矩阵完成。
|
||||
- PRD未给error-free Port表示法、App绕过Port的标准库I/O精确限制、跨Port Adapter setting owner;这些应在实现前由主代理用既有范围做一致决定并同步设计。
|
||||
- 明确区分“Port已声明failure全集”和“所有实现实际failure并集”;后者不能替代前者。
|
||||
- 查不到单独 `.trellis/spec/backend/application-testing.md`,应用测试规则当前在compiler-contracts与toolchain文档;不要引用不存在的规范文件。
|
||||
- 未运行测试,未修改源码/规范/状态。
|
||||
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"id": "port-adapter",
|
||||
"name": "port-adapter",
|
||||
"title": "Dever Port 与 Adapter",
|
||||
"description": "实现可替换 Port 合同、Adapter 实现和 setting.json 绑定,并收紧角色调用方向",
|
||||
"status": "completed",
|
||||
"dev_type": null,
|
||||
"scope": null,
|
||||
"package": null,
|
||||
"priority": "P1",
|
||||
"creator": "Antigravity",
|
||||
"assignee": "Antigravity",
|
||||
"createdAt": "2026-09-20",
|
||||
"completedAt": "2026-09-20",
|
||||
"branch": "main",
|
||||
"base_branch": "main",
|
||||
"worktree_path": null,
|
||||
"commit": null,
|
||||
"pr_url": null,
|
||||
"subtasks": [],
|
||||
"children": [],
|
||||
"parent": "09-20-production-application-language",
|
||||
"relatedFiles": [],
|
||||
"notes": "",
|
||||
"meta": {}
|
||||
}
|
||||
Reference in new issue
Block a user