Skip to content

Latest commit

 

History

History
160 lines (123 loc) · 9.78 KB

File metadata and controls

160 lines (123 loc) · 9.78 KB

索引刷新策略收敛 — 实施计划

日期:2026-07-30 设计:.agents/docs/2026-07-30-issue315-index-refresh-policy-design.md 关联:#315 目标版本:2026.7.30.3(基线 d79ab00 / 2026.7.30.2) 形态:单 PR,内部按 A–F 六段推进,每段自带验收。 状态:已完成(PR #320)。实施中偏离计划的地方记在 §G。


0. 基线核对(已完成)

事实 位置(@ d79ab00)
TTL 站点 src/build/prepare.cppm:1390-1433
每个依赖已自带有序候选 DependencySpec.candidatessrc/pm/dep_spec.cppm:46)—— 与 mcpp add 用的是同一份
存在性查找 mcpp::pm::lookup_descriptor(route, candidates)src/pm/index_route.cppm:150
route 工厂 prepare.cppm:1548index_route(cfg))、commands.cppm:151-152
版本可满足性 mcpp::pm::resolve_semver(ns, short, req, route, platform)src/pm/resolver.cppm:96),失败即不可满足
索引目录/marker/age xlings.cppm:381-490匿名命名空间,未导出
索引状态(已导出) default_index_status / official_index_statusIndexStatus{dir,present,fresh,ageSeconds}
索引内容 rev <indexDir>/.xlings-index-version(实测 mcpplibs=8d67478,xim-pkgindex=ebf4020)—— mcpp 零引用
跨平台锁原语 mcpp::platform::fs::FileLock::try_acquire(dir)src/platform/fs.cppm:60bmi_cache.cppm:241 已在用)
索引数据根(已导出) mcpp::xlings::paths::index_data(env)

A. 探针(先跑,不写代码)

  • P-1 .xlings-index-version 更新语义:clean-room MCPP_HOME,走一次真实 xlings update, 比对刷新前后该文件的值与格式(是否 7 位短 sha、有无尾随空白、artifact/git 两通道是否都写)。 降级预案:若某通道不写 → index_revision() 返回 nullopt,advisory 只显示 age,gate 不受影响
  • P-2 INV-3 误报实测:对一个同时含 mcpplibs 依赖与 xim 依赖的工程,打印每个依赖的 RefreshDecision,确认 xim 落 SuppressedInconclusive 而非 DescriptorMiss。 P-2 依赖 A 段代码,故实际在 B 段末尾以单测 + 一次真实工程 -v 运行完成。

验收:P-1 结论写回设计文档 §10;P-2 由单测 + e2e #6 承接。


B. 策略模块(新增 src/pm/index_refresh.cppm

导出面

enum class RefreshReason { None, IndexAbsent, DescriptorMiss, VersionMiss,
                           SuppressedDebounce, SuppressedOffline, SuppressedInconclusive };
struct RefreshPolicy   { bool offline; std::int64_t debounceSeconds = 120; };
struct RefreshDecision { bool shouldRefresh; RefreshReason reason; std::string subject; };

RefreshDecision decide_for_dependency(const IndexRoute&, const DependencySpec&,
                                      const xlings::Env&, const platform::PlatformKey&,
                                      const RefreshPolicy&);
std::expected<void,std::string> apply(const RefreshDecision&, const xlings::Env&, bool quiet);
RefreshPolicy policy_from_env(const config::GlobalConfig&, bool offlineFlag);
std::string_view reason_text(RefreshReason);

判据顺序(短路,单测逐行锁)

  1. spec.isPath() || spec.isGit()None
  2. policy.offlineSuppressedOffline
  3. 无候选路由到内置 registryfind_for_ns 为 null 或 is_builtin)→ None (项目 path/自定义 git 索引:刷全局索引对它无用,保持现状语义)
  4. lookup_descriptor 未命中且 !conclusiveSuppressedInconclusiveINV-3
  5. 默认索引 pkgs/ 不存在 → IndexAbsent → 刷
  6. 未命中 → DescriptorMiss → 刷
  7. 命中 + is_version_constraint(spec.version) + resolve_semver 失败 → VersionMiss → 刷
  8. 上面判刷但 0 <= age < debounceSecondsSuppressedDebounce

依赖方向index_refresh → {index_route, resolver, dep_spec, xlings, config, platform.fs, ui, log}。resolver 已 import index_route,无环。

验收tests/unit/test_pm_index_refresh.cpp 覆盖 8 条判据 + 精确版本不判 VersionMiss。


C. xlings 侧最小增补(src/xlings.cppm

  1. IndexStatus 增字段 std::optional<std::string> rev(读 .xlings-index-version,trim 空白; 缺失/空 → nullopt)。→ mcpp index status 与 advisory 免费获得。
  2. 新导出 std::optional<std::string> index_revision(const std::filesystem::path& indexDir)
  3. S1is_index_dir_freshage < 0 → 判 stale(未来时间戳不得等于永远新鲜)。
  4. S3update_index 内部用 FileLock::try_acquire(paths::index_data(env)) 包裹; 拿不到锁 → 记 verbose 日志并返回 0(跳过,不报错、不阻塞)
  5. S4mark_known_indexes_refreshed 去掉 projectDir 早退,改为项目模式下也打标 (仅供 advisory)。

验收tests/unit/test_xlings.cpp 增负 age、rev 读取三态(缺失/空/带换行)。


D. 五站点收敛

站点 动作
prepare.cppm:1390-1433 整段替换:遍历 m->dependenciesdecide_for_dependency;命中即 apply 一次(进程内 bool once),reason 进 -v 日志
commands.cppm:167(add) 改为 decide_* + apply,删除本地 TTL 判断
xlings.cppm:1400(xim 门) 保留判据,kJustRefreshedSeconds 改由 RefreshPolicy::debounceSeconds 提供默认值,注释指向策略模块
package_fetcher.cppm:1060 保留,输出走统一句式
index_management.cppm:29(search) 保留 TTL,加注释说明这是唯一允许时间驱动的站点

验收grep -n "is_index_fresh" src/ 只剩 search 一处 + xlings 内部实现。


E. D6 + 表达面

  1. mcpp update 不再空转commands.cppm:403-430):先强制刷索引(不看 TTL/debounce, offline 下拒绝并说明),再清 lock 条目,输出 mcpplibs 8d67478 → a1b2c3dalready at …
  2. --offlinebuild / run / test / update 注册;语义 = 本次调用不发起任何网络 (索引 + 包 + 工具链自动安装)。
  3. MCPP_OFFLINE=1:等价 env;MCPP_NO_AUTO_INSTALL 保留为兼容别名(等价于 offline 的 工具链子集),help/docs 标注 deprecated。
  4. [index] auto_refresh = true|falseconfig.cppm 读取,默认 true。
  5. 可解释输出:刷新一行说明主语与原因;-v 打印跳过原因与索引 rev/age; offline+miss 的结构化错误含 rev + age + mcpp index update
  6. mcpp index status 增 rev 列。

优先级:flag > env > config。


F. 测试 + 版本 + 交付

  • 新 e2e tests/e2e/173_index_refresh_policy.sh,9 个场景(设计 §8)。 反向断言写法out=$(...); echo "$out" | grep -q X && { echo FAIL; exit 1; }! cmd | grep 在 errexit 下被豁免,永不失败)。
  • 全量本地回归:mcpp test(单测)+ 受影响 e2e(09/14/44/105/169 等涉及索引/离线的用例)。
  • 版本 2026.7.30.3:只改 mcpp.toml + src/toolchain/fingerprint.cppmbootstrap pin(.xlings.json / ci-fresh-install.yml MCPP_PIN)不动bash .github/tools/check_version_pins.sh 必须过。
  • 单 PR → CI 全绿 → bypass squash 合入。
  • 合入后:release 四平台 → 镜像 xlings-res(gh+gtc)→ xim-pkgindex PR → xlings install mcpp 真装验证 → bootstrap pin bump(独立 commit)。

G. 实施中相对本计划的偏离(都是实现时才看清的约束)

计划 实际 原因
--offlineBuildOverrides 透传 CLI 全局 flag → MCPP_OFFLINE 环境变量,各处按需读 消费者横跨 pm(索引/包)与 build(工具链),透传要改三个子系统的签名;env 又恰好让 --offlineMCPP_OFFLINE=1 成为字面上的同一个开关
offline_mode()mcpp.pm.index_refresh mcpp.platform.env(叶子模块) mcpp.pm.index_route 已 import mcpp.fetcher,pm 里的 helper 会让 fetcher 反向依赖 pm ⇒ 模块环
判据顺序:offline 最先短路 本地分析在前,opt-out 在后 先短路会把「解析得好好的依赖」也报成 offline mode,而那恰恰是 offline 用户要确认的事。多出的成本只是本地文件读
decide_for_miss(policy, subject) env 参数,同样吃去抖 连着两次 mcpp add 打错字不该换来两次多仓库同步,理由与构建路径完全一致
mcpp update 无条件强制刷新 工程里没有走共享 registry 的依赖时跳过 纯 path/git 依赖的工程刷全局索引毫无意义;顺带让 e2e 23/24 不必平白联网
index_revision 直接定义在实现区 拆成匿名命名空间里的 read_index_revision + 外层转发 实现区那一大段在匿名命名空间内,直接定义会得到内部链接 ⇒ 导出声明未定义(link 期才暴露)
P1 单列一期 全部并入 P0 advisory、index status rev 列、S4 打标、docs 都只有几十行,分期反而让「变懒」上线时缺少配套的可观测性
计划未列 追加 mcpp why depspackage index: <rev> (<age>) 刷新变懒后,「为什么解析到这个版本」的答案常常是「因为它是你本地索引已知的最新版」

风险与回退

风险 处置
INV-3 接错 → 每次构建都刷 单测 + e2e #6 双闸;P-2 真实工程实测
.xlings-index-version 语义不符 gate 不依赖 rev,最坏只损失 advisory 精度
S3 锁在 Windows 行为不一致 platform/fs.cppm 封装;拿不到锁=跳过而非阻塞
语义变化引发「拉不到新版本」误报 mcpp update 变成真入口 + advisory 提示 + docs 明写