fix(m193): 复审 r2——台账三处失效引用、M39 注释逐字还原、schema doc 数字补守卫
- P2-1:docs 的契约表把 retry_limit 范围从 (1,10) 改成 0–10(0 与 1 都 = 只试一次), 与 §4 和 keys.rs 对齐;§2 与 §6 的守卫名统一成 the_schema_contract_is_single_sourced (原两条已合并),并把变异 M9(去掉 .max(1) ⇒ 红)补进 §6 台账。 - P3-4:keys.rs 的 M39(MCP server)既有注释按 origin/dev 原文逐字还原(含「同一安全策略、 同一审计日志」);腾出的 1 行用「去掉 alias use、两处常量改全路径」换,没有压任何既有论证。 比对:版本史 / D20 / M39 三段与 origin/dev 逐字节相同。 - P3-2:SettingSpec.doc 是 &'static str(concat! 插不进常量),描述里的数字是第三处第二来源, 现在有守卫 the_key_docs_quote_the_real_limits(改 1~64 → 1~99 ⇒ 红,变异 M10 已跑)。 §6.1.1 用表登记三处「数字的第二来源」现状。 - P3-1:docs 里那个游离反引号删掉。
This commit is contained in:
1 parent
94f56f9d9c
commit
54dde7f494
3 files changed
+46
-25
No files matched your search
@@ -184,8 +184,8 @@ impl DownloadTuning {
|
||||
}
|
||||
}
|
||||
|
||||
/// 每个缓存根一份「宿主想要什么」(`engine_for` 构造时用它,见 [`wanted`])。按数据根而不是进程级:
|
||||
/// 登记处本就是「一个数据根一个引擎」;进程级那份在多数据根时会互相踩(A 根改 2、B 根冲回 8)。
|
||||
/// 每个缓存根一份「宿主想要什么」([`wanted`] 用)。按数据根而不是进程级:登记处本就是「一个数据根
|
||||
/// 一个引擎」;进程级那份在多数据根时会互相踩(A 根改 2、B 根冲回 8)。
|
||||
fn wanted_slot() -> &'static Mutex<HashMap<PathBuf, DownloadTuning>> {
|
||||
static WANTED: OnceLock<Mutex<HashMap<PathBuf, DownloadTuning>>> = OnceLock::new();
|
||||
WANTED.get_or_init(|| Mutex::new(HashMap::new()))
|
||||
@@ -228,10 +228,8 @@ mod tests {
|
||||
use crate::setting::keys::{launcher_schema, launcher_version};
|
||||
|
||||
/// **守门③**:schema 的默认值 / 区间 / 键名与 `EngineConfig::default()` / 调优默认值取同一组常量。
|
||||
///
|
||||
/// 变异证明(只改实现侧):`EngineConfig::default()` 的并发写回字面量 64 ⇒ 红;schema 默认值
|
||||
/// 写成字面量 16 ⇒ 红;`MAX_CONCURRENCY` 改了而 schema 仍写 64 ⇒ 红。这条防的正是「两处各写
|
||||
/// 一个数」长回来(8 vs 64 就是这么长出来的)。
|
||||
/// 变异证明(只改实现侧):`EngineConfig::default()` 的并发写回 64、schema 默认值写成 16、
|
||||
/// `MAX_CONCURRENCY` 改了而 schema 仍写 64 ⇒ 各自红。
|
||||
#[test]
|
||||
fn the_schema_contract_is_single_sourced() {
|
||||
let schema = launcher_schema();
|
||||
@@ -275,6 +273,17 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
/// `SettingSpec.doc` 是 `&'static str`(`concat!` 插不进常量),描述里的数字只能手写——这条钉住它,
|
||||
/// 改常量不改文案就红(`catalog.rs` 会把这些 doc 当 JSON schema 的 description 发出去)。
|
||||
#[test]
|
||||
fn the_key_docs_quote_the_real_limits() {
|
||||
let schema = launcher_schema();
|
||||
let doc = |key: &str| schema.spec(key).expect("键已登记").doc;
|
||||
assert!(doc(KEY_CONCURRENCY).contains(&format!("1~{MAX_CONCURRENCY}")));
|
||||
assert!(doc(KEY_CONCURRENCY).contains(&format!("出厂 {DEFAULT_MAX_CONCURRENCY}")));
|
||||
assert!(doc(KEY_RETRY_LIMIT).contains(&format!("出厂 {DEFAULT_MAX_ATTEMPTS_PER_SOURCE}")));
|
||||
}
|
||||
|
||||
/// 未填 / 越界回落默认(不阻断下载);**`retry_limit: 0` 是合法值**,语义 = 只试一次。
|
||||
///
|
||||
/// 变异证明:把并发的区间过滤删掉 ⇒ `0` / `9999` 两条红;把尝试次数的 `.max(1)` 删掉 ⇒
|
||||
@@ -330,9 +339,8 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
/// `apply_gh_proxy` 的三档真的改到档案上、并且**改到候选链上**(`MirrorTable::expand()` 读的
|
||||
/// 就是档案里那个前缀)。端到端(schema 键 → 调优 → `plan()`)由 `shared.rs` 与 presenter 守。
|
||||
/// 变异证明:把 `Off` 分支改成原样返回 `profile` ⇒ `off` 那条红。
|
||||
/// `apply_gh_proxy` 的三档真的改到档案上、并且**改到候选链上**(`expand()` 读的就是档案里那个
|
||||
/// 前缀;自定义前缀那一档的链断言在 `shared.rs`)。变异证明:`Off` 分支改成原样返回 ⇒ `off` 红。
|
||||
#[test]
|
||||
fn gh_proxy_override_reaches_the_profile_and_the_chain() {
|
||||
use crate::download::mirrors::{MirrorTable, SOURCE_GH_PROXY};
|
||||
@@ -353,21 +361,20 @@ mod tests {
|
||||
assert_eq!(kept.gh_proxy, mirror_first.gh_proxy, "Builtin 不该动档案");
|
||||
assert!(
|
||||
sources(&kept).contains(&SOURCE_GH_PROXY.to_string()),
|
||||
"出厂默认套前缀"
|
||||
"出厂套前缀"
|
||||
);
|
||||
|
||||
let off = DownloadTuning::from_settings(None, None, Some("off"))
|
||||
.apply_gh_proxy(mirror_first.clone());
|
||||
assert!(!off.gh_proxy_enabled());
|
||||
assert!(
|
||||
!sources(&off).contains(&SOURCE_GH_PROXY.to_string()),
|
||||
"关了就没有候选"
|
||||
"关了就没候选"
|
||||
);
|
||||
|
||||
let prefix = DownloadTuning::from_settings(None, None, Some("https://gh.example/"))
|
||||
.apply_gh_proxy(mirror_first);
|
||||
assert_eq!(prefix.gh_proxy.as_deref(), Some("https://gh.example/"));
|
||||
assert!(prefix.gh_proxy_enabled());
|
||||
assert!(prefix.gh_proxy_enabled(), "前缀生效");
|
||||
}
|
||||
|
||||
/// 每个数据根的调优互不串味(一个根改成 2 不该改另一个根的引擎)。
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
use super::catalog::SettingSchema;
|
||||
use super::schema::{SchemaId, SchemaVersion};
|
||||
use super::spec::{AiPolicy, SettingSpec};
|
||||
use crate::download::config as download;
|
||||
|
||||
/// `core.data_mode` 允许值。
|
||||
pub const DATA_MODES: &[&str] = &["portable", "standard", "custom"];
|
||||
@@ -105,15 +104,15 @@ pub fn launcher_schema() -> SettingSchema {
|
||||
))
|
||||
.with(SettingSpec::int(
|
||||
"download.concurrency",
|
||||
download::DEFAULT_MAX_CONCURRENCY as i64,
|
||||
(1, download::MAX_CONCURRENCY as i64),
|
||||
crate::download::config::DEFAULT_MAX_CONCURRENCY as i64,
|
||||
(1, crate::download::config::MAX_CONCURRENCY as i64),
|
||||
AiPolicy::silent(),
|
||||
"同时下载的文件数(1~64,出厂 8)。这是引擎真实持有的并发许可,不是展示值;越界或手改坏值按出厂值生效",
|
||||
))
|
||||
.with(SettingSpec::int(
|
||||
"download.retry_limit",
|
||||
download::DEFAULT_MAX_ATTEMPTS_PER_SOURCE as i64,
|
||||
(0, download::MAX_ATTEMPTS_PER_SOURCE as i64),
|
||||
crate::download::config::DEFAULT_MAX_ATTEMPTS_PER_SOURCE as i64,
|
||||
(0, crate::download::config::MAX_ATTEMPTS_PER_SOURCE as i64),
|
||||
AiPolicy::silent(),
|
||||
"单个下载源的最多尝试次数(含首次;出厂 3)。0 与 1 都等于「只试一次、不重试」——0 是旧版 GUI 提供过的档位,保留合法但不代表「不下载」。换源不受此限",
|
||||
))
|
||||
@@ -228,8 +227,9 @@ pub fn launcher_schema() -> SettingSchema {
|
||||
AiPolicy::locked(),
|
||||
"LLM API 密钥——AI 永不读写的凭据(D9)",
|
||||
))
|
||||
// M39(P3 MCP server,02 §0「传输定论」):对外 agent 工具服务。三个开关默认「关 / 只读」——
|
||||
// 外部客户端拿到的是与内置客户端**同一注册表**的工具,但写权限必须用户显式打开(02 §1)。
|
||||
// M39(P3 MCP server,02 §0「传输定论」):对外 agent 工具服务。
|
||||
// 三个开关默认「关 / 只读」——外部客户端拿到的是与内置客户端**同一注册表**的工具,
|
||||
// 但写权限必须用户显式打开(02 §1「同一注册表、同一安全策略、同一审计日志」)。
|
||||
.with(SettingSpec::bool(
|
||||
"agent.mcp.enabled",
|
||||
false,
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
| 键 | schema 默认 / 范围 | 引擎侧消费者 | 真实生效值(改造前) | 本轮 |
|
||||
|---|---|---|---|---|
|
||||
| `download.concurrency` | 8 / 1–256 | **无**(恒用 `EngineConfig::default()`) | **64**(`Semaphore` 容量) | ✅ 接进引擎,范围收成 1–64 |
|
||||
| `download.retry_limit` | 3 / 0–10 | **无** | 3(碰巧与 schema 相同,但改它无效) | ✅ 接进引擎,范围 (1,10) |
|
||||
| `download.retry_limit` | 3 / 0–10 | **无** | 3(碰巧与 schema 相同,但改它无效) | ✅ 接进引擎,范围 0–10(0 与 1 都 = 只试一次) |
|
||||
| `download.gh_proxy` | (M197 登记,dev 上尚无此键) | JDK 作业那条(M197 自建引擎时覆盖档案) | 地区档案里的内置值(镜像优先时 `gh-proxy.com`) | ✅ 下沉到共享装配点,所有下载路径一致 |
|
||||
| `download.region` | auto / auto·cn·global | **无**(`RegionProfile::pinned` 无生产调用点) | 永远按「时区+语言」自动判定 | ❌ 未接(见 §5) |
|
||||
| `download.mirror_preference` | auto / auto·official·mirror | **无** | 永远按地区模式(镜像优先/直连) | ❌ 未接(见 §5) |
|
||||
@@ -39,8 +39,9 @@ crates/core/src/download/config.rs
|
||||
└─→ setting::keys 的 schema 默认值与范围 (界面/AI/CLI 看到的契约)
|
||||
```
|
||||
|
||||
守卫 `the_schema_defaults_are_the_engine_defaults`(`download/config.rs`)与
|
||||
`the_hard_caps_are_single_sourced` 让「改一处另一个不变」直接变红——这是防 8 vs 64 复发的唯一手段。
|
||||
守卫 `the_schema_contract_is_single_sourced`(`download/config.rs`,它合并了原先的「默认值相等」与
|
||||
「区间同源」两条)让「改一处另一个不变」直接变红——这是防 8 vs 64 复发的唯一手段。
|
||||
另有 `the_key_docs_quote_the_real_limits` 钉住 schema 描述里的数字(见 §6.1 的第三处字面量)。
|
||||
|
||||
## 3. 接线链(宿主 → 引擎)
|
||||
|
||||
@@ -99,7 +100,7 @@ config/launcher.json
|
||||
**`download.retry_limit = 0` 是合法值**(P2-1 复审的结论):旧版 GUI 的档位表里就有 `0`,所以
|
||||
范围保留 `0~10`,语义定为 **`0` 与 `1` 都 = 只试一次、不重试**(`attempts = max(1, value)`)。
|
||||
这样老配置零迁移、`ull config get` 读到的 0 与引擎真做的事一致(试 1 次),不会再出现
|
||||
「界面显示 0、引擎按 3 跑」。`)
|
||||
「界面显示 0、引擎按 3 跑」。
|
||||
|
||||
## 5. 没接的四个键(已登记为 finding)
|
||||
|
||||
@@ -121,10 +122,12 @@ config/launcher.json
|
||||
| M2 | `shared::build` 的 `tune.apply_gh_proxy(region)` 换回 `region` | 同上 | 红(`off` 档仍有 gh-proxy 候选) |
|
||||
| M3 | 删掉 `set_tuning` 里的 `registry().remove` | 同上 | 红(换参数后交回旧引擎) |
|
||||
| M4 | 删掉构造点的 `clamp(1, MAX_CONCURRENCY)` | 同上 | 红(9999 张许可真开了) |
|
||||
| M5 | `EngineConfig::default()` 的并发写回字面量 64 | `download::config::tests::the_schema_defaults…` | 红 |
|
||||
| M5 | `EngineConfig::default()` 的并发写回字面量 64 | `download::config::tests::the_schema_contract_is_single_sourced` | 红 |
|
||||
| M6 | schema 的并发默认值写成字面量 16 | 同上 | 红(两处各写一个数) |
|
||||
| M7 | 删掉 `host/engine.rs::engine` 里的 `apply_tuning` | `host::settings::tests::the_concurrency_setting…` | 红(探针 8,期望 2) |
|
||||
| M8 | 下载设置页的 gh-proxy 行改回 `("Preferred source", "Automatic (region)")` | `the_downloads_rows_state_semantics…` | 红 |
|
||||
| M9 | `from_settings` 的尝试次数去掉 `.max(1)`(0 原样交给引擎) | `download::config::tests::missing_or_illegal_values_fall_back_to_the_defaults` | 红(`left: 0 / right: 1`) |
|
||||
| M10 | schema 描述里的「1~64」改成「1~99」(第三处字面量) | `download::config::tests::the_key_docs_quote_the_real_limits` | 红 |
|
||||
|
||||
## 6.1 已知的观感问题(P3-1,归属下一条 mission)
|
||||
|
||||
@@ -138,6 +141,17 @@ config/launcher.json
|
||||
(同一条 finding 里还记着 presenter 侧描述里的 `1~64` / 出厂值:presenter 已改成用常量 `format!`,
|
||||
iced 侧的 `rows` 是 `&'static str`,仍是**已知的第二真相源**,等真控件一并解决。)
|
||||
|
||||
### 6.1.1 三处「数字的第二来源」现状(P3-2 登记)
|
||||
|
||||
| 位置 | 能不能引用常量 | 守卫 |
|
||||
|---|---|---|
|
||||
| `presenter/src/host/settings/download.rs` 的行描述 | 能(`format!`) | 已改 |
|
||||
| `app-iced/src/pages/settings/download.rs` 的 `rows` | 不能:`&'static [(&'static str, &'static str)]` 是编译期字面量,要改运行时生成得动 `Group` 的类型 | 无(`the_downloads_rows_state_semantics…` 只断言 `"1~64"` 这个字符串在,改常量不会红) |
|
||||
| `setting/keys.rs` 的 `SettingSpec.doc` | 不能:`doc: &'static str`(`spec.rs:183`),`concat!` 只吃字面量、插不进常量 | ✅ `the_key_docs_quote_the_real_limits`(`config.rs`):把「1~64」「出厂 8」「出厂 3」与常量比对,改常量不改文案就红 |
|
||||
|
||||
`SettingSpec.doc` 那一处**不是注释**:`catalog.rs` 会把它当 JSON schema 的 `description` 发出去
|
||||
(AI 工具与文档可见),所以它必须有守卫——这也是三处里唯一补上了守卫的一处。
|
||||
|
||||
## 7. 没验的部分
|
||||
|
||||
* **真实网络下的并发/重试行为未验**:守卫读的是 `Semaphore` 的真实许可数与 `attempts_for()` 的
|
||||
|
||||
Reference in new issue
Block a user