@antprofuse/saddle-skill 0.3.5 → 0.3.6

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.5 Skill 开发或审查带 profusegw 固定入口和 profusecontract 强类型外部函数调用的 Rust 业务应用。用于定义 protobuf 契约、声明强类型业务配置、编写固定 ProfuseGwContext 四参数 handler、以框架响应映射业务拒绝和技术失败,或运行正式业务门禁。
3
+ description: 使用 Saddle 0.3.6 Skill 开发或审查带 profusegw 固定入口和 profusecontract 强类型外部函数调用的 Rust 业务应用。用于定义 protobuf 契约、声明强类型业务配置、编写固定 ProfuseGwContext 四参数 handler、以框架响应映射业务拒绝和技术失败,或运行正式业务门禁。
4
4
  ---
5
5
 
6
- # Saddle 0.3.5 业务研发契约
6
+ # Saddle 0.3.6 业务研发契约
7
7
 
8
8
  业务 Agent 的直接输入只有业务 spec 与本 Skill。不要要求用户阅读 Saddle 仓库、审计报告、组件提交或内部实现。
9
9
 
@@ -14,14 +14,15 @@ description: 使用 Saddle 0.3.5 Skill 开发或审查带 profusegw 固定入口
14
14
  3. 按 [编程模型](references/programming-model.md) 声明应用、固定 profusegw operation、应用级强类型外部函数能力和唯一 handler。
15
15
  4. 按 [响应建模](references/response-model.md) 定义封闭业务码,并把业务拒绝与八类技术失败 × 三种执行确定性逐项映射为框架固定响应。禁止 `Result`、wildcard/default、空成功或自建响应外壳。
16
16
  5. 按 [本地联调与部署配置](references/local-integration.md) 配置唯一 plaintext profusecontract authority,使用固定 profusegw body 验证真实强类型调用链。
17
- 6. 业务使用数据库时,按 [数据库名称映射](references/database-name-mapping.md) 声明逻辑表和操作;物理库表名只由部署映射目录注入并在启动前冻结。
18
- 7. [正式 Gate](references/validation.md) 生成 lock,并在交付前运行:
17
+ 6. 在同一 `saddle.toml` 显式配置部署 admission 预算;业务代码不得声明或推导请求并发上限,也不得为无 DB uses 的应用伪造数据库调用。
18
+ 7. 业务使用数据库时,按 [数据库名称映射](references/database-name-mapping.md) 声明逻辑表和操作;物理库表名只由部署映射目录注入并在启动前冻结。
19
+ 8. 按 [正式 Gate](references/validation.md) 生成 lock,并在交付前运行:
19
20
 
20
21
  ```bash
21
22
  npx --no-install saddle-03-gate --manifest-path Cargo.toml
22
23
  ```
23
24
 
24
- 8. Gate 或编译失败时停止,保留最小业务 consumer 复现;不得复制或修改 Saddle 中间件绕过。
25
+ 9. Gate 或编译失败时停止,保留最小业务 consumer 复现;不得复制或修改 Saddle 中间件绕过。
25
26
 
26
27
  ## 正式版本身份
27
28
 
@@ -29,7 +30,7 @@ description: 使用 Saddle 0.3.5 Skill 开发或审查带 profusegw 固定入口
29
30
 
30
31
  ```toml
31
32
  [dependencies]
32
- saddle-framework = "=0.3.5"
33
+ saddle-framework = "=0.3.6"
33
34
  ```
34
35
 
35
36
  `saddle-framework` 是唯一业务 facade。业务不得直接依赖 `saddle-core`、`saddle-admission`、`saddle-boundary`、`saddle-db`、`saddle-macros`、`saddle-observability`、`saddle-runtime` 或 `saddle-service`。
@@ -58,7 +59,10 @@ saddle-framework = "=0.3.5"
58
59
  - 唯一配置入口是一个 `saddle.toml`,进程唯一参数为 `--config <path>`。生产启动使用绝对路径,例如 `target/release/my-app --config /etc/my-app/saddle.toml`;`--config saddle.toml` 与 `--config ./saddle.toml` 也合法,均相对当前工作目录定位文件,文件内相对路径则相对该配置文件所在目录解析。
59
60
  - profusecontract 部署只接受 `[framework.profusecontract].authority`:值为一个固定 `http://host:port` plaintext authority,或含唯一 `{zone}` 占位的模板;不得携带 URL path、凭据、TLS、服务发现或 per-call endpoint。业务 handler 不读取该配置。
60
61
  - 配置在 listener 建立前一次解析、校验、解析 secret 环境引用并冻结。普通配置不接受环境变量覆盖;secret 只能通过 `[secrets]` 明确声明的环境变量名注入。未知字段、缺失必填字段、类型错误、secret 缺失、数据库 secret/mapping 配置不成对或相对路径非法均须 fail-fast。
61
- - 文件日志只由 `[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.5,由运维侧管理归档目录容量与采集。
62
+ - `[framework.admission]` 必须声明正整数 `cpuCores` `memoryMb`;`requestTimeoutMs` 可省略,省略时固定为 5000ms。`[framework.admission.dependencies]` 必须声明正整数 `databaseConcurrency` `profusecontractConcurrency`。不存在 `maxActiveRequests`,也不得由业务代码直接指定 active/wait 容量。
63
+ - 框架以 `min(CPU、封闭单请求内存预算、Database concurrency、ProfuseContract concurrency)` 冻结 active capacity,wait capacity 固定为 `W=0`。任一资源耗尽时在 handler 前立即拒绝,不排队、不后台重试。
64
+ - ingress body、外部请求和外部响应各自受 1 MiB 静态边界约束;请求结束、断连或绝对 timeout 都必须归还同一准入资源。当前 `saddle-application/2` 没有 DB uses 时由框架完成受控 `NotUsed` 终态,业务不得增加伪 DB 调用;未来真实 DB operation 只能以 `Returned` 或 `Discarded` 终结物理资源。
65
+ - 文件日志只由 `[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.6,由运维侧管理归档目录容量与采集。
62
66
  - `[framework.management].bind` 是独立于业务 `listen` 的必填管理监听地址。管理面只接受固定 `GET /live` 与 `GET /ready`:进程处于 Starting、Ready 或 Draining 时 `/live` 返回 200;只有 Ready 时 `/ready` 返回 200,其余阶段返回 503。业务代码不得注册、覆盖或写入健康状态。
63
67
  - SIGTERM 必须先使 `/ready` 变为 503 并停止新业务流量,再 drain 已准入请求;drain 期间 `/live` 保持 200,业务入口先停止,管理入口最后停止。探针只投影本进程受管生命周期,不探测数据库、profusecontract 或其他下游;下游策略由部署环境另行负责。
64
68
 
@@ -73,8 +77,9 @@ saddle-framework = "=0.3.5"
73
77
  - handler 返回 Rust `Result`/`Err`、空成功、任意 JSON,或业务自建第二响应结构;
74
78
  - 业务拒绝或任一技术失败组合没有显式映射,机器码不稳定,或 `FailureMessage` 超过 256 bytes;
75
79
  - 出现旧响应字段、顶层 code/message 或任何旧协议兼容叙述;
76
- - Cargo 不是精确 `=0.3.5` Rust facade、缺 lock,或正式 Gate 不通过。
80
+ - Cargo 不是精确 `=0.3.6` Rust facade、缺 lock,或正式 Gate 不通过。
77
81
  - 出现旧扁平 authority 长键或旧配置类型模块路径。
78
82
  - 缺少独立 management bind、把探针挂到业务入口、添加动态管理路由,或把下游连通性等同 readiness/liveness。
83
+ - admission 预算缺失、为零/负数/溢出,出现 `maxActiveRequests`、业务自定 active/wait 容量、等待队列或资源归还旁路。
79
84
 
80
85
  停止时只交最小复现和期望业务表达,由 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.5 Skill(npm install --save-exact --ignore-scripts --no-audit --no-fund @antprofuse/saddle-skill@0.3.5),结合业务 spec 开始研发。"
4
+ default_prompt: "请安装 Saddle 0.3.6 Skill(npm install --save-exact --ignore-scripts --no-audit --no-fund @antprofuse/saddle-skill@0.3.6),结合业务 spec 开始研发。"
@@ -4,6 +4,15 @@ listen = "127.0.0.1:39101"
4
4
  [framework.management]
5
5
  bind = "127.0.0.1:39103"
6
6
 
7
+ [framework.admission]
8
+ cpuCores = 2
9
+ memoryMb = 512
10
+ requestTimeoutMs = 5000
11
+
12
+ [framework.admission.dependencies]
13
+ databaseConcurrency = 4
14
+ profusecontractConcurrency = 8
15
+
7
16
  [framework.profusecontract]
8
17
  authority = "http://127.0.0.{zone}:39102"
9
18
 
@@ -4,6 +4,15 @@ listen = "0.0.0.0:8080"
4
4
  [framework.management]
5
5
  bind = "0.0.0.0:8081"
6
6
 
7
+ [framework.admission]
8
+ cpuCores = 4
9
+ memoryMb = 1024
10
+ requestTimeoutMs = 5000
11
+
12
+ [framework.admission.dependencies]
13
+ databaseConcurrency = 8
14
+ profusecontractConcurrency = 16
15
+
7
16
  [framework.profusecontract]
8
17
  authority = "http://profusecontract-{zone}.internal:50051"
9
18
 
@@ -4,6 +4,15 @@ listen = "127.0.0.1:39101"
4
4
  [framework.management]
5
5
  bind = "127.0.0.1:39103"
6
6
 
7
+ [framework.admission]
8
+ cpuCores = 2
9
+ memoryMb = 512
10
+ requestTimeoutMs = 5000
11
+
12
+ [framework.admission.dependencies]
13
+ databaseConcurrency = 4
14
+ profusecontractConcurrency = 8
15
+
7
16
  [framework.profusecontract]
8
17
  authority = "http://127.0.0.{zone}:39102"
9
18
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@antprofuse/saddle-skill",
3
- "version": "0.3.5",
4
- "description": "Saddle 0.3.5 中文 AI Coding 研发契约与业务门禁。",
3
+ "version": "0.3.6",
4
+ "description": "Saddle 0.3.6 中文 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.5"`,然后按 [最小编程模型](programming-model.md) 编写固定 `deployment_app`、显式 `business_config`、静态 operation 声明和四参数 handler。不得直接依赖任何 Saddle 中间件 crate。
49
+ 业务 crate 精确使用 `saddle-framework = "=0.3.6"`,然后按 [最小编程模型](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.5`,或 [正式 Gate](validation.md) 失败。
66
+ - `saddle-framework` 不是精确 `=0.3.6`,或 [正式 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.5 不定义 contract version、多版本路由、翻译表、alias 或 fallback。
18
+ - 0.3.6 不定义 contract version、多版本路由、翻译表、alias 或 fallback。
19
19
 
20
20
  ## 定义步骤
21
21
 
@@ -82,4 +82,4 @@ message BoundAccount {
82
82
 
83
83
  ## 变更规则
84
84
 
85
- 先修改共同契约目录并重新通过双方 conformance,再修改两侧实现。禁止两侧各自维护近似定义后人工对齐;0.3.5 遇到不兼容变化时停止并共同升级,不在运行时并存或猜测版本。
85
+ 先修改共同契约目录并重新通过双方 conformance,再修改两侧实现。禁止两侧各自维护近似定义后人工对齐;0.3.6 遇到不兼容变化时停止并共同升级,不在运行时并存或猜测版本。
@@ -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.5 部署配置只接受 `[framework.profusecontract].authority`。有业务配置的完整最小文件是 [saddle-configured.toml](../assets/saddle-configured.toml),`business_config ();` 应用使用不含 `[business]` 的 [saddle-unit.toml](../assets/saddle-unit.toml):
41
+ 0.3.6 部署配置只接受 `[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,15 @@ listen = "127.0.0.1:39101"
47
47
  [framework.management]
48
48
  bind = "127.0.0.1:39103"
49
49
 
50
+ [framework.admission]
51
+ cpuCores = 2
52
+ memoryMb = 512
53
+ requestTimeoutMs = 5000
54
+
55
+ [framework.admission.dependencies]
56
+ databaseConcurrency = 4
57
+ profusecontractConcurrency = 8
58
+
50
59
  [framework.profusecontract]
51
60
  authority = "http://127.0.0.{zone}:39102"
52
61
 
@@ -65,6 +74,11 @@ dataPath = "fixtures/data"
65
74
 
66
75
  - `[framework].listen`:必填字符串、无默认值,格式为可解析的 `IP:port`,决定实际 ProfuseGW bind 地址。
67
76
  - `[framework.management].bind`:必填字符串、无默认值,格式为独立的可解析 `IP:port`。它只承载固定 `GET /live` 与 `GET /ready`,不得与业务入口共享端口。非法地址或端口冲突必须在组件 worker 启动前 fail-fast。
77
+ - `[framework.admission].cpuCores`:必填正整数、无默认值;是部署分配给进程的 CPU 核数,不是业务期望并发。
78
+ - `[framework.admission].memoryMb`:必填正整数、无默认值;按十进制 MB 冻结部署内存预算。
79
+ - `[framework.admission].requestTimeoutMs`:可选正整数;省略时固定为 `5000` 毫秒。它覆盖同一请求的 ingress、外部调用与资源终结绝对期限,不是重试间隔。
80
+ - `[framework.admission.dependencies].databaseConcurrency`:必填正整数、无默认值;是进程可同时占用的 Database 依赖额度。
81
+ - `[framework.admission.dependencies].profusecontractConcurrency`:必填正整数、无默认值;是进程可同时占用的 ProfuseContract 依赖额度。
68
82
  - `[framework.profusecontract].authority`:必填字符串、无默认值;必须是 plaintext `http://host:port` 固定地址,或只含一个 `{zone}` 的模板。模板由同一请求的 LDC zone 在调用前解析;禁止 path、query、fragment、userinfo、凭据和其他模板。
69
83
  - `[framework.observability.logging].directory`:可选路径,默认 `./logs`;相对路径按进程当前工作目录解析。目录必须可创建并可写,否则 listener 建立前 fail-fast。
70
84
  - `[framework.observability.logging].rotation`:可选枚举,默认 `daily`,也可显式设为 `hourly`。活动文件永远是 `saddle.log`;daily 归档名为 `saddle.log.YYYY-MM-DD`,hourly 归档名为 `saddle.log.YYYY-MM-DD-HH`。
@@ -74,6 +88,10 @@ dataPath = "fixtures/data"
74
88
 
75
89
  所有配置在 listener 建立前一次解析和冻结。普通字段不能由环境变量覆盖;secret 只能通过 `[secrets]` 中明确声明的环境变量引用注入。文件不可读、未知/旧键、缺失字段、类型错误、非法 bind/authority、secret 缺失、数据库配置不完整或映射目录非法都必须 fail-fast,并返回稳定的 `saddle.process.*` 错误码。
76
90
 
91
+ 配置中没有 `maxActiveRequests`。框架从冻结输入确定唯一 active capacity:`min(CPU、封闭单请求内存额度、Database concurrency、ProfuseContract concurrency)`;wait capacity 固定为 `W=0`。超出任一维度时,请求在业务 handler 之前立即拒绝,不进入等待队列,也不执行框架后台重试。
92
+
93
+ 每个 ingress body、ProfuseContract 请求与 ProfuseContract 响应都受 1 MiB 边界约束,框架还把解码、响应和固定开销计入同一封闭内存额度。正常完成、客户端断连和绝对 timeout 均归还原请求的准入资源。当前 `saddle-application/2` 没有数据库 uses 时,框架自动形成 `NotUsed` 终态;业务无需且不得为了额度闭合增加伪数据库调用。未来真实数据库 operation 开始后,只能由数据库执行链形成 `Returned` 或 `Discarded` 终态。
94
+
77
95
  ## 管理探针与退出顺序
78
96
 
79
97
  - `GET /live`:Starting、Ready、Draining 均为 HTTP 200 与 `{"live":true}`;用于判断受管进程仍在推进,不表示下游可用。
@@ -82,7 +100,7 @@ dataPath = "fixtures/data"
82
100
 
83
101
  这些端点是框架生命周期的只读投影,不执行数据库查询、profusecontract RPC、磁盘探测或任意业务自检。不得在 handler 中写健康状态,不得增加动态 management route。部署系统负责探针调度、失败阈值、摘流和重启策略。
84
102
 
85
- 该 authority 是部署输入,不进入业务 spec、request 或 handler。TLS、鉴权、服务发现、负载均衡和生产凭据不属于 0.3.5;需要其中任一能力时立即停止,不得在业务 crate 自建 client。
103
+ 该 authority 是部署输入,不进入业务 spec、request 或 handler。TLS、鉴权、服务发现、负载均衡和生产凭据不属于 0.3.6;需要其中任一能力时立即停止,不得在业务 crate 自建 client。
86
104
 
87
105
  ## 联调完成条件
88
106
 
@@ -94,6 +112,6 @@ dataPath = "fixtures/data"
94
112
  4. 返回 `Completed` 或八类 `TechnicalFailure × ExecutionCertainty` 之一;业务代码显式处理,不自动重试。
95
113
  5. 受管请求只形成一个 terminal,并在写 HTTP response 前完成资源归还。
96
114
 
97
- 结构化日志至少按正式 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.5 不提供按大小轮转、归档压缩或历史清理;目录容量、外部采集、压缩与保留期由业务运维环境负责。
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.6 不提供按大小轮转、归档压缩或历史清理;目录容量、外部采集、压缩与保留期由业务运维环境负责。
98
116
 
99
117
  联调 harness 可托管 listener 与受控 profusecontract server;业务源码不得复制 harness、直接依赖中间件 crate或注册第二入口。
@@ -2,7 +2,7 @@
2
2
 
3
3
  ## 应用显式声明
4
4
 
5
- 业务应用使用 `saddle-framework = "=0.3.5"` 的公开编程面:
5
+ 业务应用使用 `saddle-framework = "=0.3.6"` 的公开编程面:
6
6
 
7
7
  ```rust
8
8
  saddle::application! {
@@ -1,4 +1,4 @@
1
- # Saddle 0.3.5 Skill 正式业务 Gate
1
+ # Saddle 0.3.6 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.5"
15
+ saddle-framework = "=0.3.6"
16
16
  ```
17
17
 
18
18
  在允许访问正式 registry 的依赖准备阶段生成并提交 `Cargo.lock`。禁止 `path`、`git`、`[patch]`、source replacement 或宽松 Saddle 版本。
@@ -25,10 +25,10 @@ saddle-framework = "=0.3.5"
25
25
  npx --no-install saddle-03-gate --manifest-path Cargo.toml
26
26
  ```
27
27
 
28
- 命令由已安装的 `@antprofuse/saddle-skill@0.3.5` 提供,不需要 Saddle 源码仓库。它会检查:
28
+ 命令由已安装的 `@antprofuse/saddle-skill@0.3.6` 提供,不需要 Saddle 源码仓库。它会检查:
29
29
 
30
30
  - facade 精确版本、禁止直接中间件依赖和 Cargo source 旁路;
31
- - `Cargo.lock` 中 admission、boundary、core、db、framework、macros、observability、runtime、service 九包统一精确映射 `0.3.5`;
31
+ - `Cargo.lock` 中 admission、boundary、core、db、framework、macros、observability、runtime、service 九包统一精确映射 `0.3.6`;
32
32
  - 唯一 `contract_dir`、目录存在、禁止旧单文件 contract 声明;
33
33
  - 目录根部 `saddle_external_function.proto` 与 Skill 权威模板逐字一致且仅出现一次,使用 option 的业务 proto 必须 direct import 根文件;
34
34
  - 编译期递归扫描/import 闭包、option-only 索引、身份唯一、请求结果类型匹配,以及所有 function 均有实际 `uses`;
@@ -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\.5"\s*(?:#.*)?$/.test(facade[1].trim())) {
34
- stop("SADDLE_VERSION", '必须直接且精确依赖 saddle-framework = "=0.3.5"');
33
+ if (!facade || !/^"=0\.3\.6"\s*(?:#.*)?$/.test(facade[1].trim())) {
34
+ stop("SADDLE_VERSION", '必须直接且精确依赖 saddle-framework = "=0.3.6"');
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\\.5"`).test(lock)) {
48
- stop("LOCK", `Cargo.lock 未绑定 ${name} 0.3.5`);
47
+ if (!new RegExp(`name = "${escaped}"\\nversion = "0\\.3\\.6"`).test(lock)) {
48
+ stop("LOCK", `Cargo.lock 未绑定 ${name} 0.3.6`);
49
49
  }
50
50
  }
51
51
  }
@@ -93,6 +93,40 @@ if (!fs.existsSync(configPath)) {
93
93
  }
94
94
  }
95
95
  if (managementBindCount !== 1) stop("MANAGEMENT", "必须且只能配置一个 [framework.management].bind");
96
+ const admission = new Map();
97
+ let admissionSection = "";
98
+ for (const line of config.split(/\r?\n/)) {
99
+ const header = line.match(/^\s*\[([^\]]+)\]\s*(?:#.*)?$/);
100
+ if (header) { admissionSection = header[1]; continue; }
101
+ const value = line.match(/^\s*([A-Za-z][A-Za-z0-9]*)\s*=\s*([^#]+?)\s*(?:#.*)?$/);
102
+ if (value && (admissionSection === "framework.admission" || admissionSection === "framework.admission.dependencies")) {
103
+ const key = `${admissionSection}.${value[1]}`;
104
+ if (admission.has(key)) stop("ADMISSION", `重复配置 ${key}`);
105
+ admission.set(key, value[2].trim());
106
+ }
107
+ }
108
+ const requiredAdmission = [
109
+ "framework.admission.cpuCores",
110
+ "framework.admission.memoryMb",
111
+ "framework.admission.dependencies.databaseConcurrency",
112
+ "framework.admission.dependencies.profusecontractConcurrency"
113
+ ];
114
+ for (const key of requiredAdmission) {
115
+ if (!admission.has(key) || !/^[1-9][0-9]*$/.test(admission.get(key))) {
116
+ stop("ADMISSION", `${key} 必须是正整数`);
117
+ }
118
+ }
119
+ const timeout = admission.get("framework.admission.requestTimeoutMs");
120
+ if (timeout !== undefined && !/^[1-9][0-9]*$/.test(timeout)) {
121
+ stop("ADMISSION", "framework.admission.requestTimeoutMs 必须是正整数;省略时固定为 5000");
122
+ }
123
+ const allowedAdmission = new Set([...requiredAdmission, "framework.admission.requestTimeoutMs"]);
124
+ for (const key of admission.keys()) {
125
+ if (!allowedAdmission.has(key)) stop("ADMISSION", `未知 admission 配置 ${key}`);
126
+ }
127
+ if (/\bmaxActiveRequests\b/.test(config)) {
128
+ stop("ADMISSION", "禁止 maxActiveRequests;active capacity 只由框架从部署预算推导");
129
+ }
96
130
  const logging = config.match(/^\s*\[framework\.observability\.logging\]\s*$([\s\S]*?)(?=^\s*\[|\s*$)/m);
97
131
  if (logging) {
98
132
  const directory = logging[1].match(/^\s*directory\s*=\s*"([^"]+)"\s*$/m);
@@ -195,4 +229,4 @@ try {
195
229
  fs.rmSync(target, {recursive: true, force: true});
196
230
  }
197
231
  if (process.exitCode) process.exit(process.exitCode);
198
- process.stdout.write("SADDLE_03_GATE PASS; SKILL=0.3.5; RUST=0.3.5; CLOSED_RESPONSE; CLOSED_CODES; FAILURE_MESSAGE_256; FAILURE_8X3; LOCKED_OFFLINE\n");
232
+ process.stdout.write("SADDLE_03_GATE PASS; SKILL=0.3.6; RUST=0.3.6; ADMISSION_DERIVED; W0; CLOSED_RESPONSE; CLOSED_CODES; FAILURE_MESSAGE_256; FAILURE_8X3; LOCKED_OFFLINE\n");