@antprofuse/saddle-skill 0.3.7 → 0.3.9

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.
package/SKILL.md CHANGED
@@ -1,9 +1,9 @@
1
1
  ---
2
2
  name: saddle-service-0-3
3
- description: 使用 Saddle 0.3.7 Skill 开发或审查带 profusegw 固定入口和 profusecontract 强类型外部函数调用的 Rust 业务应用。用于定义 protobuf 契约、声明强类型业务配置、编写固定 ProfuseGwContext 四参数 handler、以框架响应映射业务拒绝和技术失败,或运行正式业务门禁。
3
+ description: 使用 Saddle 0.3.9 Skill 开发或审查带 profusegw 固定入口和 profusecontract 强类型外部函数调用的 Rust 业务应用。用于定义 protobuf 契约、声明强类型业务配置、编写固定 ProfuseGwContext 四参数 handler、以框架响应映射业务拒绝和技术失败,或运行正式业务门禁。
4
4
  ---
5
5
 
6
- # Saddle 0.3.7 业务研发契约
6
+ # Saddle 0.3.9 业务研发契约
7
7
 
8
8
  业务 Agent 的直接输入只有业务 spec 与本 Skill。不要要求用户阅读 Saddle 仓库、审计报告、组件提交或内部实现。
9
9
 
@@ -11,7 +11,7 @@ description: 使用 Saddle 0.3.7 Skill 开发或审查带 profusegw 固定入口
11
11
 
12
12
  1. 阅读 [消费者起手式](references/consumer-start.md),只从业务 spec 提取接口、业务类型、业务规则、业务结果和外部业务能力。
13
13
  2. 先按 [外部函数协议](references/external-function-contracts.md) 在唯一 `contract_dir` 根部生成固定 `saddle_external_function.proto`,再只把具体外部函数写入业务 `.proto`。冻结目录内容后,才并行交给 Saddle 后端和 profusecontract 实现。
14
- 3. 按 [编程模型](references/programming-model.md) 声明应用、固定 profusegw operation、应用级强类型外部函数能力和唯一 handler。
14
+ 3. 按 [编程模型](references/programming-model.md) 声明应用、固定 profusegw operation、唯一 `Capabilities` 聚合与四参 handler;需要数据库时在 route 上选择生成的静态 operation
15
15
  4. 按 [响应建模](references/response-model.md) 定义封闭业务码,并把业务拒绝与八类技术失败 × 三种执行确定性逐项映射为框架固定响应。禁止 `Result`、wildcard/default、空成功或自建响应外壳。
16
16
  5. 按 [本地联调与部署配置](references/local-integration.md) 配置唯一 plaintext profusecontract authority,使用固定 profusegw body 验证真实强类型调用链。
17
17
  6. 在同一 `saddle.toml` 显式配置部署 admission 预算;业务代码不得声明或推导请求并发上限,也不得为无 DB uses 的应用伪造数据库调用。
@@ -31,7 +31,7 @@ description: 使用 Saddle 0.3.7 Skill 开发或审查带 profusegw 固定入口
31
31
 
32
32
  ```toml
33
33
  [dependencies]
34
- saddle-framework = "=0.3.7"
34
+ saddle-framework = "=0.3.9"
35
35
  ```
36
36
 
37
37
  `saddle-framework` 是唯一业务 facade。业务不得直接依赖 `saddle-core`、`saddle-admission`、`saddle-boundary`、`saddle-db`、`saddle-macros`、`saddle-observability`、`saddle-runtime` 或 `saddle-service`。
@@ -41,7 +41,7 @@ saddle-framework = "=0.3.7"
41
41
  - Saddle 编译进业务应用,不是独立服务、进程或控制面。
42
42
  - profusegw 唯一入口是 `POST /saddle/v1/ingress/profusegw/invoke`;不得手写 listener、HTTP handler 或第二路由。
43
43
  - 应用声明唯一字面量 `deployment_app`;请求 `target.app` 必须逐字匹配。每个字面量 `target.interfaceId` 只由静态 `operation_type` 分派,业务 handler 不读取或自行路由它。
44
- - profusegw body 固定且只含 `target`、`profuseGwContext`、`requestData`;`profuseGwContext.userInfo.userId` 由框架形成 `ProfuseGwContext { user_id }`,业务 payload 不得覆盖。
44
+ - profusegw body 固定且只含 `target`、`profuseGwContext`、`requestData`;`profuseGwContext.userInfo.userId` 仅在 backend 入站由框架形成只读 `ProfuseGwContext::user_id()`,业务 payload 不得覆盖。通用 ProfuseContract 技术 context 与wire 绝无 `user_id`;下游业务需要时,必须在具体 protobuf request 声明字段并由 handler 显式赋值。框架自动传播的只有 trace/rpc、LDC 和 deadline。
45
45
  - 每个应用必须声明 `business_config Type;`,每个 handler 的第四参数必须是同一 `saddle::BusinessConfig<Type>`。有配置应用从 [完整配置样例](assets/saddle-configured.toml) 起步;没有业务配置时唯一写法是 `business_config ();`,使用 [无业务配置样例](assets/saddle-unit.toml),`saddle.toml` 省略 `[business]`,测试只使用受控 `saddle::BusinessConfig::unit()`。禁止旧模块路径。
46
46
  - 所有 handler 使用 `saddle::ingress::ProfuseGwContext`;只读取正式 API 提供的只读、有界字段,不得读取 Header、Cookie、原始 JSON 或任意上下文 Map。有效 ingress traceId 原样继承,每次外部调用派生 child rpcId;业务不得解析或重写 opaque trace/rpc identity。
47
47
  - 固定调用上下文协议见 [trace/LDC 模板](assets/saddle_call_context.proto):trace 只有 `trace_id/rpc_id`,LDC 只有 `zone/idc/env`。业务请求和结果不得重复这些上下文字段。
@@ -55,6 +55,8 @@ saddle-framework = "=0.3.7"
55
55
  - 外部函数由共同 `.proto` option 的 `(puc, function)` 精确定位;应用 DSL 的 `business_unit` 必须逐字等于 option 的 `puc`。中文 `function` 逐字保留,不翻译、不 alias、不 fallback、不动态选择。
56
56
  - `functions` 只声明应用实际调用的外部函数,每项必须被至少一个 service 的 `uses` 引用;不得登记未使用能力。
57
57
  - 业务只使用生成的应用级强类型 capability,不接触 channel、stub、裸 protobuf bytes、动态函数名或通用依赖容器。
58
+ - handler 第三参数统一是生成的 `Capabilities`/其 route 类型,同时承载声明过的 ProfuseContract 方法与该 route 唯一静态 DB operation。禁止使用旧外部函数 capability 作为第三参数。
59
+ - DB route 只允许生成期选定的 query/write/单层 transaction;业务不接触 SQL、连接、transaction handle、commit/rollback 操作或动态 operation。transaction 在同一连接上执行,成功按 route 事实 commit,业务失败、timeout 或 cancel 按封闭路径 rollback/discard,不支持嵌套事务。
58
60
  - 不支持 contract 多版本路由、`Any`、开放 Map、任意 JSON、streaming、cancel 或框架自动业务重试。
59
61
  - 不创建线程、第二 Runtime、后台 task、裸 DB/网络 I/O、日志 subscriber 或第二执行面。
60
62
  - 唯一配置入口是一个 `saddle.toml`,进程唯一参数为 `--config <path>`。生产启动使用绝对路径,例如 `target/release/my-app --config /etc/my-app/saddle.toml`;`--config saddle.toml` 与 `--config ./saddle.toml` 也合法,均相对当前工作目录定位文件,文件内相对路径则相对该配置文件所在目录解析。
@@ -63,11 +65,13 @@ saddle-framework = "=0.3.7"
63
65
  - `[framework.admission]` 必须声明正整数 `cpuCores` 与 `memoryMb`;`requestTimeoutMs` 可省略,省略时固定为 5000ms。`[framework.admission.dependencies]` 必须声明正整数 `databaseConcurrency` 与 `profusecontractConcurrency`。不存在 `maxActiveRequests`,也不得由业务代码直接指定 active/wait 容量。
64
66
  - 框架以 `min(CPU、封闭单请求内存预算、Database concurrency、ProfuseContract concurrency)` 冻结 active capacity,wait capacity 固定为 `W=0`。任一资源耗尽时在 handler 前立即拒绝,不排队、不后台重试。
65
67
  - ingress body、外部请求和外部响应各自受 1 MiB 静态边界约束;请求结束、断连或绝对 timeout 都必须归还同一准入资源。当前 `saddle-application/2` 没有 DB uses 时由框架完成受控 `NotUsed` 终态,业务不得增加伪 DB 调用;未来真实 DB operation 只能以 `Returned` 或 `Discarded` 终结物理资源。
66
- - 文件日志只由 `[framework.observability.logging]` 配置;默认目录 `./logs`、默认 `daily`。活动文件固定为 `saddle.log`;daily 归档为 `saddle.log.YYYY-MM-DD`,hourly 归档为 `saddle.log.YYYY-MM-DD-HH`。应用不得安装第二 subscriber,也不得把 stdout/stderr 混入该文件。大小轮转、压缩和清理策略不属于 Saddle 0.3.7,由运维侧管理归档目录容量与采集。
68
+ - 文件日志只由 `[framework.observability.logging]` 配置;默认目录 `./logs`、默认 `daily`。活动文件固定为 `saddle.log`;daily 归档为 `saddle.log.YYYY-MM-DD`,hourly 归档为 `saddle.log.YYYY-MM-DD-HH`。应用不得安装第二 subscriber,也不得把 stdout/stderr 混入该文件。大小轮转、压缩和清理策略不属于 Saddle 0.3.9,由运维侧管理归档目录容量与采集。
67
69
  - `[framework.management].bind` 是独立于业务 `listen` 的必填管理监听地址。管理面只接受固定 `GET /live` 与 `GET /ready`:进程处于 Starting、Ready 或 Draining 时 `/live` 返回 200;只有 Ready 时 `/ready` 返回 200,其余阶段返回 503。业务代码不得注册、覆盖或写入健康状态。
70
+ - 同一独立 management listener 还固定提供 `GET /metrics`,只读导出进程内有界快照。标签集合封闭且低基数,不得携带 request/trace/rpc/user/payload/authority/error text 或任意业务值;业务不得注册第二 metrics 端点。
71
+ - `[framework.lifecycle]` 只接受正整数 `startTimeoutMs` 与 `shutdownTimeoutMs`,两者省略时均为 `30000`。shutdown 值是从 SIGTERM 起覆盖停止接收、drain、逆序 shutdown、post-driver 与资源 ZERO 的唯一绝对总预算,不能分段重置。
68
72
  - SIGTERM 必须先使 `/ready` 变为 503 并停止新业务流量,再 drain 已准入请求;drain 期间 `/live` 保持 200,业务入口先停止,管理入口最后停止。探针只投影本进程受管生命周期,不探测数据库、profusecontract 或其他下游;下游策略由部署环境另行负责。
69
73
  - 框架结构化日志必须能以同一 `trace_id` 还原 ingress → admission → handler → DB/NotUsed → ProfuseContract → response → resource finalization,并包含容量 limit/used/bottleneck/reject reason、child `rpc_id`/zone/安全 authority、生命周期/健康和 logger dropped/output failure。完整字段与查询方式见 [生产日志还原](references/operations-observability.md)。
70
- - 日志不得包含 body、Header、secret、token、Database URL、SQL/参数或物理表列;业务和运维不得依赖这些内容。0.3.7 不引入 Metrics、Prometheus 或 OTLP。
74
+ - 日志不得包含 body、Header、secret、token、Database URL、SQL/参数或物理表列;业务和运维不得依赖这些内容。0.3.9 不引入 Metrics、Prometheus 或 OTLP。
71
75
 
72
76
  ## 立即停止
73
77
 
@@ -80,10 +84,11 @@ saddle-framework = "=0.3.7"
80
84
  - handler 返回 Rust `Result`/`Err`、空成功、任意 JSON,或业务自建第二响应结构;
81
85
  - 业务拒绝或任一技术失败组合没有显式映射,机器码不稳定,或 `FailureMessage` 超过 256 bytes;
82
86
  - 出现旧响应字段、顶层 code/message 或任何旧协议兼容叙述;
83
- - Cargo 不是精确 `=0.3.7` Rust facade、缺 lock,或正式 Gate 不通过。
87
+ - Cargo 不是精确 `=0.3.9` Rust facade、缺 lock,或正式 Gate 不通过。
84
88
  - 出现旧扁平 authority 长键或旧配置类型模块路径。
85
89
  - 缺少独立 management bind、把探针挂到业务入口、添加动态管理路由,或把下游连通性等同 readiness/liveness。
86
90
  - admission 预算缺失、为零/负数/溢出,出现 `maxActiveRequests`、业务自定 active/wait 容量、等待队列或资源归还旁路。
87
91
  - 排障依赖 body/Header/secret/token/Database URL/SQL/参数/物理表列,或要求第二 logger、Metrics/Prometheus/OTLP、任意日志 Map。
92
+ - handler 第三参数不是生成的 `Capabilities`,DB operation 未在 route 静态声明,或业务要求 raw/dynamic SQL、嵌套事务、连接/transaction handle、commit/rollback 选择。
88
93
 
89
94
  停止时只交最小复现和期望业务表达,由 Saddle 修复公共能力缺口。
@@ -1,4 +1,4 @@
1
1
  interface:
2
2
  display_name: "Saddle 0.3 业务应用"
3
3
  short_description: "使用 Saddle 0.3 开发受控边界的 Rust 业务应用"
4
- default_prompt: "请安装 Saddle 0.3.7 Skill(npm install --save-exact --ignore-scripts --no-audit --no-fund @antprofuse/saddle-skill@0.3.7),结合业务 spec 开始研发。"
4
+ default_prompt: "请安装 Saddle 0.3.9 Skill(npm install --save-exact --ignore-scripts --no-audit --no-fund @antprofuse/saddle-skill@0.3.9),结合业务 spec 开始研发。"
@@ -4,6 +4,10 @@ listen = "127.0.0.1:39101"
4
4
  [framework.management]
5
5
  bind = "127.0.0.1:39103"
6
6
 
7
+ [framework.lifecycle]
8
+ startTimeoutMs = 30000
9
+ shutdownTimeoutMs = 30000
10
+
7
11
  [framework.admission]
8
12
  cpuCores = 2
9
13
  memoryMb = 512
@@ -4,6 +4,10 @@ listen = "0.0.0.0:8080"
4
4
  [framework.management]
5
5
  bind = "0.0.0.0:8081"
6
6
 
7
+ [framework.lifecycle]
8
+ startTimeoutMs = 30000
9
+ shutdownTimeoutMs = 30000
10
+
7
11
  [framework.admission]
8
12
  cpuCores = 4
9
13
  memoryMb = 1024
@@ -4,6 +4,10 @@ listen = "127.0.0.1:39101"
4
4
  [framework.management]
5
5
  bind = "127.0.0.1:39103"
6
6
 
7
+ [framework.lifecycle]
8
+ startTimeoutMs = 30000
9
+ shutdownTimeoutMs = 30000
10
+
7
11
  [framework.admission]
8
12
  cpuCores = 2
9
13
  memoryMb = 512
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@antprofuse/saddle-skill",
3
- "version": "0.3.7",
4
- "description": "Saddle 0.3.7 中文 AI Coding 研发契约与业务门禁。",
3
+ "version": "0.3.9",
4
+ "description": "Saddle 0.3.9 中文 AI Coding 研发契约与业务门禁。",
5
5
  "license": "MIT OR Apache-2.0",
6
6
  "files": ["SKILL.md", "agents", "references", "scripts", "assets"],
7
7
  "bin": {"saddle-03-gate": "scripts/saddle-03-gate.js"},
@@ -46,7 +46,7 @@ Saddle 在编译期从 `contract_dir` 确定性递归收集 `.proto`,闭合 im
46
46
 
47
47
  ## 阶段 3:Rust 实现
48
48
 
49
- 业务 crate 精确使用 `saddle-framework = "=0.3.7"`,然后按 [最小编程模型](programming-model.md) 编写固定 `deployment_app`、显式 `business_config`、静态 operation 声明和四参数 handler。不得直接依赖任何 Saddle 中间件 crate。
49
+ 业务 crate 精确使用 `saddle-framework = "=0.3.9"`,然后按 [最小编程模型](programming-model.md) 编写固定 `deployment_app`、显式 `business_config`、静态 operation 声明和四参数 handler。不得直接依赖任何 Saddle 中间件 crate。
50
50
 
51
51
  实现通过 Gate 后,按 [本地联调](local-integration.md) 由外部 harness 注入唯一 plaintext profusecontract authority,并以固定 profusegw body 验证真实 typed call。endpoint、request/call identity 和 deadline 都是框架或部署输入,不写入业务 spec。
52
52
 
@@ -63,6 +63,6 @@ Saddle 在编译期从 `contract_dir` 确定性递归收集 `.proto`,闭合 im
63
63
  - 缺少固定 `deployment_app`,或 app/interfaceId/endpoint 被做成动态业务输入;
64
64
  - 技术失败码或执行确定性存在 catch-all/default;
65
65
  - 需要裸 gRPC/channel/bytes、第二入口、动态 descriptor 或未声明外部 I/O;
66
- - `saddle-framework` 不是精确 `=0.3.7`,或 [正式 Gate](validation.md) 失败。
66
+ - `saddle-framework` 不是精确 `=0.3.9`,或 [正式 Gate](validation.md) 失败。
67
67
 
68
68
  停止产物只包含最小复现、缺失身份和期望业务表达,不包含中间件替代实现。
@@ -15,7 +15,7 @@
15
15
  - `puc` 是业务单元,例如 `puc`。
16
16
  - `function` 是契约原始函数名,允许中文,例如 `查询用户绑定户号`。
17
17
  - protobuf method、Rust 方法和 Java 方法使用各语言合法的本地标识符;它们不是跨系统身份。
18
- - 0.3.7 不定义 contract version、多版本路由、翻译表、alias 或 fallback。
18
+ - 0.3.9 不定义 contract version、多版本路由、翻译表、alias 或 fallback。
19
19
 
20
20
  ## 定义步骤
21
21
 
@@ -66,6 +66,8 @@ message BoundAccount {
66
66
  }
67
67
  ```
68
68
 
69
+ `QueryBoundAccountsRequest.user_id` 是该具体业务 protobuf request 显式声明的 payload 字段,必须由 handler 从入站 `ProfuseGwContext::user_id()` 主动赋值。它不是 ProfuseContract 通用技术上下文字段,框架不会隐式复制。
70
+
69
71
  这里的跨系统正式身份是 `("puc", "查询用户绑定户号")`。`QueryBoundAccounts`、未来生成的 Rust 方法名和 Java 方法名都只是本地代码标识符,不能替换、翻译或推导中文 `function`。
70
72
 
71
73
  该 import 必须精确指向同目录根部的 `saddle_external_function.proto`。其 bytes 来自 Rust Alpha.6 公开闭包的权威模板并由 Skill 固定携带;不得自行创建同名 extension,也不得复制为另一份协议。
@@ -82,4 +84,4 @@ message BoundAccount {
82
84
 
83
85
  ## 变更规则
84
86
 
85
- 先修改共同契约目录并重新通过双方 conformance,再修改两侧实现。禁止两侧各自维护近似定义后人工对齐;0.3.7 遇到不兼容变化时停止并共同升级,不在运行时并存或猜测版本。
87
+ 先修改共同契约目录并重新通过双方 conformance,再修改两侧实现。禁止两侧各自维护近似定义后人工对齐;0.3.9 遇到不兼容变化时停止并共同升级,不在运行时并存或猜测版本。
@@ -22,7 +22,7 @@ profusegw 只向固定路径发送通用 JSON body;业务接口不新增 URL
22
22
  - `target.app` 必须逐字等于应用 DSL 的 `deployment_app`。
23
23
  - `target.interfaceId` 必须逐字等于某个 service 的 `operation_type`,并由生成的静态表唯一分派。
24
24
  - `requestData` 严格解码为该 service 的具名 request。
25
- - `userId` 由框架以只读 `ProfuseGwContext::user_id()` 交给 handler,业务不得从 `requestData`、Header 或任意 Map 覆盖。
25
+ - `userId` 仅在 backend 入站由框架以只读 `ProfuseGwContext::user_id()` 交给 handler,业务不得从 `requestData`、Header 或任意 Map 覆盖。通用 ProfuseContract 技术上下文不含、也不自动复制该身份。
26
26
  - `traceId/rpcId` 是 opaque identity:有效 ingress traceId 原样继承,外部函数调用派生 child rpcId。`zone/idc/env` 只读传播,业务 payload 不得复制这些上下文字段。
27
27
 
28
28
  固定 HTTP profile 是 `POST /saddle/v1/ingress/profusegw/invoke` 与 `application/json`。本地 harness 必须给出受控 request identity、call identity 和绝对 deadline;不要在业务 handler 中构造它们。
@@ -38,7 +38,7 @@ target/release/my-saddle-app --config /etc/my-saddle-app/saddle.toml
38
38
 
39
39
  生产推荐传绝对配置路径。`--config saddle.toml` 与 `--config ./saddle.toml` 也合法,它们都相对进程当前工作目录定位配置文件;配置中的 `mappingDir` 和业务 `resolve_path()` 则相对 `saddle.toml` 所在目录解析。进程只接受这一对参数,不从默认位置搜索、不合并多文件、不热更新。
40
40
 
41
- 0.3.7 部署配置只接受 `[framework.profusecontract].authority`。有业务配置的完整最小文件是 [saddle-configured.toml](../assets/saddle-configured.toml),`business_config ();` 应用使用不含 `[business]` 的 [saddle-unit.toml](../assets/saddle-unit.toml):
41
+ 0.3.9 部署配置只接受 `[framework.profusecontract].authority`。有业务配置的完整最小文件是 [saddle-configured.toml](../assets/saddle-configured.toml),`business_config ();` 应用使用不含 `[business]` 的 [saddle-unit.toml](../assets/saddle-unit.toml):
42
42
 
43
43
  ```toml
44
44
  [framework]
@@ -47,6 +47,10 @@ listen = "127.0.0.1:39101"
47
47
  [framework.management]
48
48
  bind = "127.0.0.1:39103"
49
49
 
50
+ [framework.lifecycle]
51
+ startTimeoutMs = 30000
52
+ shutdownTimeoutMs = 30000
53
+
50
54
  [framework.admission]
51
55
  cpuCores = 2
52
56
  memoryMb = 512
@@ -73,7 +77,9 @@ dataPath = "fixtures/data"
73
77
  字段合同:
74
78
 
75
79
  - `[framework].listen`:必填字符串、无默认值,格式为可解析的 `IP:port`,决定实际 ProfuseGW bind 地址。
76
- - `[framework.management].bind`:必填字符串、无默认值,格式为独立的可解析 `IP:port`。它只承载固定 `GET /live` 与 `GET /ready`,不得与业务入口共享端口。非法地址或端口冲突必须在组件 worker 启动前 fail-fast。
80
+ - `[framework.management].bind`:必填字符串、无默认值,格式为独立的可解析 `IP:port`。它只承载固定 `GET /live`、`GET /ready` 与 `GET /metrics`,不得与业务入口共享端口。非法地址或端口冲突必须在组件 worker 启动前 fail-fast。
81
+ - `[framework.lifecycle].startTimeoutMs`:可选正整数,默认 `30000` 毫秒;限定受管组件启动等待。超时为 typed 启动失败,回滚已进入组件且不发布 ready。
82
+ - `[framework.lifecycle].shutdownTimeoutMs`:可选正整数,默认 `30000` 毫秒;从 SIGTERM 起算,共同覆盖停止接收、请求 drain、逆序 shutdown、post-driver 和资源 ZERO 的唯一总预算。超时记录 typed 阶段并 fail-closed,不伪报 clean shutdown。
77
83
  - `[framework.admission].cpuCores`:必填正整数、无默认值;是部署分配给进程的 CPU 核数,不是业务期望并发。
78
84
  - `[framework.admission].memoryMb`:必填正整数、无默认值;按十进制 MB 冻结部署内存预算。
79
85
  - `[framework.admission].requestTimeoutMs`:可选正整数;省略时固定为 `5000` 毫秒。它覆盖同一请求的 ingress、外部调用与资源终结绝对期限,不是重试间隔。
@@ -90,28 +96,29 @@ dataPath = "fixtures/data"
90
96
 
91
97
  配置中没有 `maxActiveRequests`。框架从冻结输入确定唯一 active capacity:`min(CPU、封闭单请求内存额度、Database concurrency、ProfuseContract concurrency)`;wait capacity 固定为 `W=0`。超出任一维度时,请求在业务 handler 之前立即拒绝,不进入等待队列,也不执行框架后台重试。
92
98
 
93
- 每个 ingress body、ProfuseContract 请求与 ProfuseContract 响应都受 1 MiB 边界约束,框架还把解码、响应和固定开销计入同一封闭内存额度。正常完成、客户端断连和绝对 timeout 均归还原请求的准入资源。当前 `saddle-application/2` 没有数据库 uses 时,框架自动形成 `NotUsed` 终态;业务无需且不得为了额度闭合增加伪数据库调用。未来真实数据库 operation 开始后,只能由数据库执行链形成 `Returned` 或 `Discarded` 终态。
99
+ 每个 ingress body、ProfuseContract 请求与 ProfuseContract 响应都受 1 MiB 边界约束,框架还把解码、响应和固定开销计入同一封闭内存额度。正常完成、客户端断连和绝对 timeout 均归还原请求的准入资源。无 DB route 由框架形成 `NotUsed`;真实 DB route 由生成的 query/write/单层 transaction 执行链形成 `Returned` 或 `Discarded`。DB 值返回 handler 生成响应后,框架在写回、断连或 timeout 终态到达时才 finish 请求;业务不得提前终结或丢弃资源 owner。
94
100
 
95
101
  ## 管理探针与退出顺序
96
102
 
97
103
  - `GET /live`:Starting、Ready、Draining 均为 HTTP 200 与 `{"live":true}`;用于判断受管进程仍在推进,不表示下游可用。
98
104
  - `GET /ready`:只有所有受管组件启动完成后的 Ready 阶段为 HTTP 200 与 `{"ready":true}`;Starting、Draining 等非 Ready 阶段为 HTTP 503 与 `{"ready":false}`。
105
+ - `GET /metrics`:在同一独立 management listener 返回 OpenMetrics-compatible 进程内有界快照。指标名与 label 集合固定、低基数;禁止 request/trace/rpc/user/payload/authority/error text、secret 或业务值作为 label。
99
106
  - SIGTERM 到达后,框架先原子清除 readiness,使负载均衡停止新流量,再等待已准入请求 drain。drain 期间 management listener 与 `/live` 保持可用;业务 listener 先停止,management listener 最后停止,进程退出后两者均不可达。
100
107
 
101
108
  这些端点是框架生命周期的只读投影,不执行数据库查询、profusecontract RPC、磁盘探测或任意业务自检。不得在 handler 中写健康状态,不得增加动态 management route。部署系统负责探针调度、失败阈值、摘流和重启策略。
102
109
 
103
- 该 authority 是部署输入,不进入业务 spec、request 或 handler。TLS、鉴权、服务发现、负载均衡和生产凭据不属于 0.3.7;需要其中任一能力时立即停止,不得在业务 crate 自建 client。
110
+ 该 authority 是部署输入,不进入业务 spec、request 或 handler。TLS、鉴权、服务发现、负载均衡和生产凭据不属于 0.3.9;需要其中任一能力时立即停止,不得在业务 crate 自建 client。
104
111
 
105
112
  ## 联调完成条件
106
113
 
107
114
  本地联调必须观察到:
108
115
 
109
116
  1. 固定 profusegw body 被唯一静态 operation 接受;错误 app 被拒绝,未知 interfaceId 不进入 handler。
110
- 2. handler 读取同一请求的 `user_id`,发起一次生成的强类型调用。
111
- 3. profusecontract 收到同一 request identity、派生 call identity、绝对 deadline、`user_id`、`business_unit + function` 和强类型 payload。
117
+ 2. handler 如果下游业务确实需要用户身份,将读取到的 `user_id` 显式赋给该具体 protobuf request 声明的业务字段;框架不提供通用复制帮助。
118
+ 3. profusecontract 收到同一 request identity、派生 call identity、绝对 deadline、trace/rpc 与 LDC 技术上下文、`business_unit + function` 和强类型 payload;通用外呼 context/wire 不包含 `user_id`。自动传播仅限 trace/rpc、LDC 和 deadline
112
119
  4. 返回 `Completed` 或八类 `TechnicalFailure × ExecutionCertainty` 之一;业务代码显式处理,不自动重试。
113
- 5. 受管请求只形成一个 terminal,并在写 HTTP response 前完成资源归还。
120
+ 5. 受管请求只形成一个 terminal;DB 物理资源先收敛,但准入 finish 仅在 HTTP response 写回、断连或 timeout 之后完成。
114
121
 
115
- 结构化日志至少按正式 API 暴露的字段记录 trace/rpc、span/parent、request/call、app/interface、zone、outcome 与 duration;不得记录 payload、Cookie、session、原始 context、PII、secret 或数据库值。业务不得安装第二日志 subscriber。`saddle.log` 是框架结构化日志;stdout/stderr 是独立进程流,运维采集时不得与文件日志拼接成同一记录源。Saddle 0.3.7 不提供按大小轮转、归档压缩或历史清理;目录容量、外部采集、压缩与保留期由业务运维环境负责。
122
+ 结构化日志至少按正式 API 暴露的字段记录 trace/rpc、span/parent、request/call、app/interface、zone、outcome 与 duration;不得记录 payload、Cookie、session、原始 context、PII、secret 或数据库值。业务不得安装第二日志 subscriber。`saddle.log` 是框架结构化日志;stdout/stderr 是独立进程流,运维采集时不得与文件日志拼接成同一记录源。Saddle 0.3.9 不提供按大小轮转、归档压缩或历史清理;目录容量、外部采集、压缩与保留期由业务运维环境负责。
116
123
 
117
124
  联调 harness 可托管 listener 与受控 profusecontract server;业务源码不得复制 harness、直接依赖中间件 crate或注册第二入口。
@@ -1,6 +1,6 @@
1
1
  # 生产日志还原与排障
2
2
 
3
- Saddle 0.3.7 的框架事件只写入同一 `saddle.log`。运维应以 ingress 提供的 `trace_id` 为主键,在单个进程文件顺序内还原请求,不依赖请求 body、Header 或数据库细节。
3
+ Saddle 0.3.9 的框架事件只写入同一 `saddle.log`。运维应以 ingress 提供的 `trace_id` 为主键,在单个进程文件顺序内还原请求,不依赖请求 body、Header 或数据库细节。
4
4
 
5
5
  ## 还原一条请求
6
6
 
@@ -42,4 +42,8 @@ jq -c 'select(
42
42
 
43
43
  日志不得包含 request/response body、任意 Header、Cookie、secret、token、密码、Database URL/连接串、SQL、SQL 参数、数据库值、映射后的物理表名或列名。ProfuseContract `authority` 只能是无凭据目标标签,不是 URL、路径、userinfo、query 或 fragment。
44
44
 
45
- 排障不得依赖上述禁止内容,也不得为补充观测而安装第二 logger、Metrics、PrometheusOTLP、业务 subscriber 或任意键值 Map。需要业务字段时,只记录经业务安全审查的有界业务结果码;不要复制 payload。
45
+ 排障不得依赖上述禁止内容,也不得为补充观测而安装第二 logger、metrics endpoint、Prometheus/OTLP exporter、业务 subscriber 或任意键值 Map。需要业务字段时,只记录经业务安全审查的有界业务结果码;不要复制 payload。
46
+
47
+ ## 固定 metrics 边界
48
+
49
+ 运维只从已配置的 management bind 读取 `GET /metrics`。该端点投影进程内有界快照,指标名及 label 集合由框架封闭。label 必须低基数,不包含 request/trace/rpc/user/payload/authority/error text、secret 或业务值。Skill 不引入第二 metrics 端点、Prometheus 客户端或 OTLP。
@@ -2,7 +2,7 @@
2
2
 
3
3
  ## 应用显式声明
4
4
 
5
- 业务应用使用 `saddle-framework = "=0.3.7"` 的公开编程面:
5
+ 业务应用使用 `saddle-framework = "=0.3.9"` 的公开编程面:
6
6
 
7
7
  ```rust
8
8
  saddle::application! {
@@ -40,6 +40,16 @@ saddle::application! {
40
40
 
41
41
  `deployment_app` 是本部署唯一固定应用身份。profusegw body 的 `target.app` 必须逐字匹配它;每个 service 的 `operation_type` 是对应 `target.interfaceId` 的静态身份。两者都是声明期字面量,不是 handler 参数或动态路由键。
42
42
 
43
+ 0.3.9 起,handler 的第三参数是同一个应用能力聚合:所有 route 都保留声明过的
44
+ ProfuseContract 强类型方法;需要数据库的 route 另外在 service 声明中选择唯一静态
45
+ operation,例如 `database query_optional find_user (generated::FindUser);`、
46
+ `database write save_user (generated::SaveUser);`、`database transaction_commit save_user
47
+ (generated::SaveUserCommit);` 或 `database transaction_rollback save_user
48
+ (generated::SaveUserRollback);`。对应 handler 使用 `Capabilities<Route>`,且只得到该 route
49
+ 选择的方法。未声明数据库的 handler 继续使用 `Capabilities`,类型上没有数据库方法,
50
+ 框架以 `NotUsed` 结束。transaction 的 commit/rollback 是 route 的生成期事实,不是业务
51
+ 参数;业务侧没有连接、SQL、动态 operation 或 transaction handle。
52
+
43
53
  `functions` 只写当前应用实际使用的外部函数。每项必须由至少一个 service 的 `uses` 引用;普通 RPC、纯消息 proto 和未带正式 method option 的 RPC 不写入 `functions`。
44
54
 
45
55
  业务 proto option 使用字段 `puc`;应用 DSL 使用 `business_unit`。两者必须逐字相等,均不允许翻译、alias 或 fallback。
@@ -52,18 +62,18 @@ saddle::application! {
52
62
  async fn query_my_bound_accounts(
53
63
  request: QueryMyBoundAccountsRequest,
54
64
  context: saddle::ingress::ProfuseGwContext,
55
- contract: PucProfuseContract,
65
+ capabilities: Capabilities<QueryMyBoundAccounts>,
56
66
  config: saddle::BusinessConfig<BusinessSettings>,
57
67
  ) -> saddle::ingress::ProfuseGwResponse<QueryMyBoundAccountsResult, BusinessCode>;
58
68
  ```
59
69
 
60
70
  - `request`:纯业务接口输入。
61
- - `context`:固定 profusegw 框架上下文;除 `user_id` 外还只读提供 opaque trace/rpc 与 LDC zone/idc/env。有效 traceId 原样继承,每次外部调用由框架派生 child rpcId;业务不得解析、拼接或重写。
62
- - `contract`:应用级强类型 profusecontract 调用能力,只包含应用显式登记的函数;不是通用依赖注入容器。
71
+ - `context`:固定 profusegw 入站框架上下文;只读提供 `user_id` opaque trace/rpc 与 LDC zone/idc/env。有效 traceId 原样继承,每次外部调用由框架派生 child rpcId;业务不得解析、拼接或重写。通用 ProfuseContract context/wire 没有 `user_id`,自动传播仅限 trace/rpc、LDC 和 deadline;下游需要用户身份时,由 handler 读取 `context.user_id()` 并显式赋给具体 protobuf request 声明的业务字段。
72
+ - `capabilities`:应用生成的唯一能力聚合;保留应用声明的强类型 ProfuseContract 方法,并且只向 DB route 暴露该 route 选定的一个静态 DB 方法。无 DB route 用 `Capabilities`,数据库 route 用 `Capabilities<Route>`。
63
73
  - `config`:与应用 `business_config` 声明同型、在启动时解析校验并冻结的配置。通过 `config.get()` 读取,通过 `config.resolve_path()` 按 `saddle.toml` 所在目录解析业务相对路径。
64
74
  - 返回值:框架固定 `ProfuseGwResponse<Data, Code>`;详细成功、业务拒绝与技术失败建模见 [响应建模](response-model.md)。禁止 Rust `Result` 和自建 JSON 外壳。
65
75
 
66
- 所有 profusegw operation 必须使用同一个固定 `ProfuseGwContext`。业务只能读取其只读 `user_id`;禁止用自建 context、Header/JSON Map 或通用依赖容器补位。
76
+ 所有 profusegw operation 必须使用同一个固定 `ProfuseGwContext`。业务只能读取其入站只读 `user_id`;禁止用自建 context、Header/JSON Map 或通用依赖容器补位,也不得假定框架会将该身份隐式传给 ProfuseContract。
67
77
 
68
78
  每个应用必须显式声明业务配置类型。没有业务配置时也不能省略声明,唯一写法是:
69
79
 
@@ -78,7 +88,7 @@ business_config ();
78
88
  生成的强类型 async 调用必须等待结果,并分别处理完成结果与技术失败:
79
89
 
80
90
  ```rust
81
- match contract.query_bound_accounts(input).await {
91
+ match capabilities.query_bound_accounts(input).await {
82
92
  ExternalFunctionResult::Completed(result) => map_business_result(result),
83
93
  ExternalFunctionResult::TechnicalFailure(failure) => match failure.code() {
84
94
  // 穷举固定技术失败,不允许 wildcard/default。
@@ -86,6 +96,22 @@ match contract.query_bound_accounts(input).await {
86
96
  }
87
97
  ```
88
98
 
99
+ ## 静态数据库 route
100
+
101
+ service 内只允许下列声明形状,operation 类型由应用的生成步骤产出,不得由业务手写 SQL 实现:
102
+
103
+ ```rust
104
+ database query_optional query (generated::FindUser);
105
+ database write write (generated::SaveUser);
106
+ database transaction_commit transaction (generated::SaveUserCommit);
107
+ database transaction_rollback rollback (generated::SaveUserRollback);
108
+ ```
109
+
110
+ - 无 DB route 没有数据库方法,框架以 `NotUsed` 结束。query 和 write 只消费生成的有界参数/行类型。
111
+ - 单层 transaction 的操作与 commit/rollback 决策都是 route 的生成期事实;同一连接成功后 commit,失败、timeout 或 cancel 时 rollback,无法证明可复用时 discard。业务不得调用 commit/rollback,不得嵌套 transaction。
112
+ - DB 物理终态后,handler 先使用返回值形成响应;框架保留私有 post-DB terminal,直到响应写回成功、断连或 timeout 后才 finish 原请求并归还准入资源。
113
+ - raw SQL、动态 SQL/操作名、物理表列、pool/连接、transaction handle 都不是业务 API。
114
+
89
115
  `Completed` 只表示函数形成确定的契约 result,不等于函数业务成功。`TechnicalFailure` 不携带业务 result,并带 `NOT_EXECUTED`、`EXECUTED` 或 `MAY_HAVE_EXECUTED` 执行确定性。
90
116
 
91
117
  在实现前先为以下八项各写一条显式业务决策;正式 Rust 枚举名必须从身份匹配的 API 读取,不得根据本表猜模块或类型:
@@ -1,4 +1,4 @@
1
- # Saddle 0.3.7 Skill 正式业务 Gate
1
+ # Saddle 0.3.9 Skill 正式业务 Gate
2
2
 
3
3
  ## 依赖与锁
4
4
 
@@ -12,7 +12,7 @@ edition = "2024"
12
12
  rust-version = "1.95"
13
13
 
14
14
  [dependencies]
15
- saddle-framework = "=0.3.7"
15
+ saddle-framework = "=0.3.9"
16
16
  ```
17
17
 
18
18
  在允许访问正式 registry 的依赖准备阶段生成并提交 `Cargo.lock`。禁止 `path`、`git`、`[patch]`、source replacement 或宽松 Saddle 版本。
@@ -25,20 +25,23 @@ saddle-framework = "=0.3.7"
25
25
  npx --no-install saddle-03-gate --manifest-path Cargo.toml
26
26
  ```
27
27
 
28
- 命令由已安装的 `@antprofuse/saddle-skill@0.3.7` 提供,不需要 Saddle 源码仓库。它会检查:
28
+ 命令由已安装的 `@antprofuse/saddle-skill@0.3.9` 提供,不需要 Saddle 源码仓库。它会检查:
29
29
 
30
30
  - facade 精确版本、禁止直接中间件依赖和 Cargo source 旁路;
31
- - `Cargo.lock` 中 admission、boundary、core、db、framework、macros、observability、runtime、service 九包统一精确映射 `0.3.7`;
31
+ - `Cargo.lock` 中 admission、boundary、core、db、framework、macros、observability、runtime、service 九包统一精确映射 `0.3.9`;
32
32
  - 唯一 `contract_dir`、目录存在、禁止旧单文件 contract 声明;
33
33
  - 目录根部 `saddle_external_function.proto` 与 Skill 权威模板逐字一致且仅出现一次,使用 option 的业务 proto 必须 direct import 根文件;
34
34
  - 编译期递归扫描/import 闭包、option-only 索引、身份唯一、请求结果类型匹配,以及所有 function 均有实际 `uses`;
35
35
  - 固定 `ProfuseGwContext`、应用声明、八类技术失败与三种 certainty 均被显式引用;
36
+ - handler 第三参数统一为生成 `Capabilities`,DB route 只选择 query/write/单层 transaction 的生成静态 operation;
37
+ - lifecycle 只有两个正整数超时(省略均为 30000),management 固定包含 `/live`、`/ready`、`/metrics`;
36
38
  - `application/2` 显式 `business_config Type`(无配置为 `()`),handler 四参数接收同型冻结 `BusinessConfig<Type>`;
37
39
  - 部署配置使用唯一正式键 `[framework.profusecontract].authority`;有配置与 unit 应用分别使用 Skill 的完整样例,unit 文件不得出现 `[business]`;旧长键与旧配置类型模块路径均拒绝;
38
40
  - handler 直接返回框架 `ProfuseGwResponse`,应用声明封闭 `response_code`,业务码实现 `ProfuseGwCode` 并注册无碰撞稳定机器码;
39
41
  - 失败只使用 `ProfuseGwFailure { code, FailureMessage }`,拒绝旧字段、重复分类、顶层 code/message、Rust `Result`/`Err`、第二响应外壳、任意 JSON、无界展示文案、空成功及技术失败 wildcard/default;
40
42
  - 唯一字面量 `deployment_app`、静态 `operation_type`,并拒绝业务代码读取或动态路由 `target.app/interfaceId`;
41
43
  - 无 wildcard/default、第二 listener/Runtime、裸 gRPC/HTTP、动态函数或 raw invoke;
44
+ - 无 raw/dynamic SQL、嵌套事务、业务 commit/rollback 控制或低基数集合之外的 metrics label;
42
45
  - Rust 1.95 下 `cargo check --locked --offline`。
43
46
 
44
47
  Gate 只验证业务 consumer 的静态边界与可编译性;本地真实 I/O 由 [联调完成条件](local-integration.md) 验证。plaintext authority 是部署配置,不得塞入业务源码以绕过 Gate。
@@ -30,8 +30,8 @@ if (/^\s*\[(patch|replace)\b/m.test(cargo) || /\b(path|git|branch|tag)\s*=/.test
30
30
  stop("DEPENDENCY_SOURCE", "禁止 path/git/patch/replace 依赖旁路");
31
31
  }
32
32
  const facade = cargo.match(/^\s*saddle-framework\s*=\s*(.+)$/m);
33
- if (!facade || !/^"=0\.3\.7"\s*(?:#.*)?$/.test(facade[1].trim())) {
34
- stop("SADDLE_VERSION", '必须直接且精确依赖 saddle-framework = "=0.3.7"');
33
+ if (!facade || !/^"=0\.3\.9"\s*(?:#.*)?$/.test(facade[1].trim())) {
34
+ stop("SADDLE_VERSION", '必须直接且精确依赖 saddle-framework = "=0.3.9"');
35
35
  }
36
36
  const internals = ["saddle-core","saddle-admission","saddle-boundary","saddle-db","saddle-macros","saddle-observability","saddle-runtime","saddle-service"];
37
37
  for (const name of internals) {
@@ -44,8 +44,8 @@ if (!fs.existsSync(lockPath)) {
44
44
  const publicClosure = ["saddle-admission", "saddle-boundary", "saddle-core", "saddle-db", "saddle-framework", "saddle-macros", "saddle-observability", "saddle-runtime", "saddle-service"];
45
45
  for (const name of publicClosure) {
46
46
  const escaped = name.replaceAll("-", "\\-");
47
- if (!new RegExp(`name = "${escaped}"\\nversion = "0\\.3\\.7"`).test(lock)) {
48
- stop("LOCK", `Cargo.lock 未绑定 ${name} 0.3.7`);
47
+ if (!new RegExp(`name = "${escaped}"\\nversion = "0\\.3\\.9"`).test(lock)) {
48
+ stop("LOCK", `Cargo.lock 未绑定 ${name} 0.3.9`);
49
49
  }
50
50
  }
51
51
  }
@@ -93,6 +93,18 @@ if (!fs.existsSync(configPath)) {
93
93
  }
94
94
  }
95
95
  if (managementBindCount !== 1) stop("MANAGEMENT", "必须且只能配置一个 [framework.management].bind");
96
+ const lifecycle = new Map();
97
+ let lifecycleSection = "";
98
+ for (const line of config.split(/\r?\n/)) {
99
+ const header = line.match(/^\s*\[([^\]]+)\]\s*(?:#.*)?$/);
100
+ if (header) { lifecycleSection = header[1]; continue; }
101
+ const value = line.match(/^\s*([A-Za-z][A-Za-z0-9]*)\s*=\s*([^#]+?)\s*(?:#.*)?$/);
102
+ if (value && lifecycleSection === "framework.lifecycle") lifecycle.set(value[1], value[2].trim());
103
+ }
104
+ for (const [key, value] of lifecycle) {
105
+ if (!new Set(["startTimeoutMs", "shutdownTimeoutMs"]).has(key)) stop("LIFECYCLE", `未知 lifecycle 配置 ${key}`);
106
+ if (!/^[1-9][0-9]*$/.test(value)) stop("LIFECYCLE", `${key} 必须是正整数`);
107
+ }
96
108
  const admission = new Map();
97
109
  let admissionSection = "";
98
110
  for (const line of config.split(/\r?\n/)) {
@@ -145,6 +157,14 @@ if (!/\bbusiness_config\s+(?:\(\)|[A-Za-z_][A-Za-z0-9_:<>]*)\s*;/.test(source))
145
157
  if (!source.includes("BusinessConfig")) stop("BUSINESS_CONFIG", "handler 必须接收冻结 BusinessConfig");
146
158
  if (source.includes("saddle::config::BusinessConfig")) stop("BUSINESS_CONFIG", "公开类型路径必须是 saddle::BusinessConfig");
147
159
  if (!source.includes("ProfuseGwContext")) stop("CONTEXT", "handler 必须使用固定 ProfuseGwContext");
160
+ if (!/\bcapability\s+Capabilities\s*;/.test(source)) stop("CAPABILITIES", "application/2 第三参数聚合必须命名为 Capabilities");
161
+ if (!/\bcapabilities\s*:\s*Capabilities(?:\s*<[^>]+>)?/.test(source)) stop("CAPABILITIES", "handler 第三参数必须使用生成 Capabilities");
162
+ if (/\b(?:contract|client)\s*:\s*[A-Za-z_][A-Za-z0-9_]*(?:Contract|Client)\b/.test(source)) stop("CAPABILITIES", "禁止旧第三参数 capability/client");
163
+ for (const db of source.matchAll(/\bdatabase\s+([A-Za-z_][A-Za-z0-9_]*)\s+/g)) {
164
+ if (!new Set(["query_optional", "write", "transaction_commit", "transaction_rollback"]).has(db[1])) {
165
+ stop("DATABASE", `禁止非静态或未批准数据库模式 ${db[1]}`);
166
+ }
167
+ }
148
168
  if (!source.includes("ProfuseGwResponse")) stop("RESPONSE", "handler 必须直接返回框架 ProfuseGwResponse");
149
169
  if (!source.includes("ProfuseGwCode")) stop("RESPONSE_CODE", "业务码必须实现 ProfuseGwCode");
150
170
  if (!source.includes("FailureMessage::new")) stop("FAILURE_MESSAGE", "失败响应必须使用 256-byte 有界 FailureMessage");
@@ -209,6 +229,7 @@ const forbidden = [
209
229
  ["WILDCARD", /_\s*=>/], ["LISTENER", /\b(TcpListener|axum|hyper|actix_web|warp)\b/],
210
230
  ["RUNTIME", /\b(tokio::runtime|Runtime::new|spawn\s*\()/], ["RAW_RPC", /\b(tonic|Channel|ProfuseContractBoundary|raw_invoke|invoke_raw)\b/],
211
231
  ["DYNAMIC_FUNCTION", /\b(dynamic_function|call_dynamic|DynamicExternalFunction)\b/],
232
+ ["RAW_DYNAMIC_DATABASE", /\b(raw_sql|dynamic_sql|query_dynamic|execute_raw|transaction_handle|begin_transaction|nested_transaction)\b/],
212
233
  ["DYNAMIC_INGRESS_IDENTITY", /\b(target\.app|target\.interfaceId|interface_id\s*\()/],
213
234
  ["HANDLER_RESULT", /async\s+fn\s+[A-Za-z_][A-Za-z0-9_]*[\s\S]{0,1000}->\s*(?:std::result::)?Result\s*</],
214
235
  ["ARBITRARY_JSON", /\bserde_json::(?:Value|json!)\b/],
@@ -229,4 +250,4 @@ try {
229
250
  fs.rmSync(target, {recursive: true, force: true});
230
251
  }
231
252
  if (process.exitCode) process.exit(process.exitCode);
232
- process.stdout.write("SADDLE_03_GATE PASS; SKILL=0.3.7; RUST=0.3.7; ADMISSION_DERIVED; W0; CLOSED_RESPONSE; CLOSED_CODES; FAILURE_MESSAGE_256; FAILURE_8X3; LOCKED_OFFLINE\n");
253
+ process.stdout.write("SADDLE_03_GATE PASS; SKILL=0.3.9; RUST=0.3.9; ADMISSION_DERIVED; W0; CLOSED_RESPONSE; CLOSED_CODES; FAILURE_MESSAGE_256; FAILURE_8X3; LOCKED_OFFLINE\n");