For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (
- [ ]) syntax for tracking.
Goal: 按设计文档 2026-07-22-issue267-269-index-artifact-and-org-migration-design.md 落地 D1–D5,单 PR 携版本 0.0.103,合入后走发布闭环 + xlings 全生态验证。
Architecture: 新/旧 URL 与 artifact base 收敛为 mcpp::config 三常量;canonicalize_legacy_index_names 导出并承担"URL 归一→名字迁移→artifact 内存级填充→去重"单循环;文件层治愈只做最小文本手术(config.toml URL 替换、.xlings.json URL 替换 + artifact 注入,幂等闸=已含 res base);seed 管线由 pair 升级为 SeedRepo{name,url,artifact,source},非空才发射,零 diff 门。
Tech Stack: C++23 modules(自托管 mcpp build/mcpp test)、gtest 单测(tests/unit)、bash e2e(tests/e2e)、gh CLI。
- 版本:
mcpp.tomlversion 与src/toolchain/fingerprint.cppmMCPP_VERSION 同 commit 同步为0.0.103(不一致 release smoke 失败)。 - 新 URL
https://github.com/mcpplibs/mcpp-index.git;旧 URLhttps://github.com/mcpp-community/mcpp-index.git;artifact basehttps://github.com/xlings-res/mcpp-index。 - 无 artifact 的仓 seed 输出逐字节不变(零 diff 门)。
.xlings.json迁移不整文件重生成(保 subos/版本绑定),只做目标文本替换,双间距变体(mcpp pretty / xlings compact)。mcpp.fallback.config_migration是叶子模块,不 import mcpp.config —— URL 字面量在此重复是接受的(与现有 name 迁移同风格)。- 构建/测试命令:
mcpp build;MCPP_FRESH=$(realpath $(find target -type f -name mcpp -printf '%T@ %p\n' | sort -rn | head -1 | cut -d' ' -f2));"$MCPP_FRESH" test;e2e 单跑MCPP="$MCPP_FRESH" bash tests/e2e/<n>.sh。 - 分支:
feat/index-artifact-0.0.103offorigin/main(a768f48);单 PR;合并gh pr merge --squash --admin。
-
git checkout -b feat/index-artifact-0.0.103 origin/main -
git add .agents/docs/2026-07-22-issue267-269-*.md && git commit -m "docs: index org migration + artifact adoption design & impl plan (#267 #269)"
Files:
- Modify:
src/config.cppm(导出区加三常量与canonicalize_legacy_index_names声明;原匿名 ns 实现移出;:360模板 URL、:575add_default URL) - Modify:
src/fallback/config_migration.cppm(两迁移函数加 URL 替换) - Modify:
src/publish/pipeline.cppm:158、docs/00-getting-started.md:98、docs/04-build-from-source.md:96、docs/zh/00-getting-started.md:99、docs/zh/04-build-from-source.md:103 - Test:
tests/unit/test_config.cpp
Interfaces (produces):
// mcpp::config, exported
inline constexpr std::string_view kMcpplibsIndexUrl = "https://github.com/mcpplibs/mcpp-index.git";
inline constexpr std::string_view kMcpplibsIndexUrlLegacy = "https://github.com/mcpp-community/mcpp-index.git";
inline constexpr std::string_view kMcpplibsIndexArtifact = "https://github.com/xlings-res/mcpp-index";
void canonicalize_legacy_index_names(GlobalConfig& cfg); // 导出(可测性)- Step 1: 失败单测(
test_config.cpp末尾追加;import mcpp.fallback.config_migration;加到文件头 import 区)
TEST(ConfigIndexMigration, CanonicalizeRewritesOrgAndNameAndFillsArtifact) {
mcpp::config::GlobalConfig cfg;
cfg.defaultIndex = "mcpp-index";
cfg.indexRepos.push_back({ "mcpp-index",
std::string(mcpp::config::kMcpplibsIndexUrlLegacy) });
mcpp::config::canonicalize_legacy_index_names(cfg);
ASSERT_EQ(cfg.indexRepos.size(), 1u);
EXPECT_EQ(cfg.defaultIndex, "mcpplibs");
EXPECT_EQ(cfg.indexRepos[0].name, "mcpplibs");
EXPECT_EQ(cfg.indexRepos[0].url, mcpp::config::kMcpplibsIndexUrl);
EXPECT_EQ(cfg.indexRepos[0].artifact, mcpp::config::kMcpplibsIndexArtifact);
}
TEST(ConfigIndexMigration, CanonicalizeFoldsLegacyAndDefaultEntries) {
mcpp::config::GlobalConfig cfg;
cfg.indexRepos.push_back({ "mcpplibs", std::string(mcpp::config::kMcpplibsIndexUrlLegacy) });
cfg.indexRepos.push_back({ "mcpplibs", std::string(mcpp::config::kMcpplibsIndexUrl) });
mcpp::config::canonicalize_legacy_index_names(cfg);
EXPECT_EQ(cfg.indexRepos.size(), 1u);
}
TEST(ConfigIndexMigration, CanonicalizeRespectsExplicitGitSourceAndForeignRepos) {
mcpp::config::GlobalConfig cfg;
mcpp::config::IndexRepo git{ "mcpplibs", std::string(mcpp::config::kMcpplibsIndexUrl) };
git.source = "git";
cfg.indexRepos.push_back(git);
cfg.indexRepos.push_back({ "other", "https://example.com/other-index.git" });
mcpp::config::canonicalize_legacy_index_names(cfg);
EXPECT_TRUE(cfg.indexRepos[0].artifact.empty());
EXPECT_TRUE(cfg.indexRepos[1].artifact.empty());
EXPECT_EQ(cfg.indexRepos[1].url, "https://example.com/other-index.git");
}
TEST(ConfigIndexMigration, MigrateConfigTomlRewritesOrgUrlIdempotently) {
auto dir = make_tempdir("mcpp-migrate-toml");
auto p = dir / "config.toml";
{ std::ofstream os(p); os <<
"[index]\ndefault = \"mcpplibs\"\n\n[index.repos.\"mcpplibs\"]\n"
"url = \"https://github.com/mcpp-community/mcpp-index.git\"\n"; }
EXPECT_TRUE(mcpp::fallback::migrate_config_toml_index_names(p));
std::stringstream ss; ss << std::ifstream(p).rdbuf();
EXPECT_NE(ss.str().find("github.com/mcpplibs/mcpp-index.git"), std::string::npos);
EXPECT_EQ(ss.str().find("mcpp-community/mcpp-index"), std::string::npos);
EXPECT_FALSE(mcpp::fallback::migrate_config_toml_index_names(p)); // 幂等
std::filesystem::remove_all(dir);
}注:IndexRepo 的 source/artifact 字段 Task 2 才加 —— 本任务与 Task 2 的实现同 commit 落(测试先全写,分两步跑)。若想严格分步,CanonicalizeRespectsExplicitGitSource 测试留到 Task 2。
- Step 2: 跑测确认编译失败(常量/导出不存在):
mcpp build期望 FAIL - Step 3: 实现——config.cppm 导出区(
struct IndexRepo后)加三常量;把canonicalize_legacy_index_names从匿名 ns 移到导出区,实现:
void canonicalize_legacy_index_names(GlobalConfig& cfg) {
if (cfg.defaultIndex == "mcpp-index") cfg.defaultIndex = "mcpplibs";
std::vector<IndexRepo> normalized;
for (auto r : cfg.indexRepos) {
// ① 组织迁移(先于名字判断与去重:老配置匹配 + 新旧条目可折叠)
if (r.url == kMcpplibsIndexUrlLegacy) r.url = std::string(kMcpplibsIndexUrl);
// ② 名字迁移(URL 已归一,只认新 URL)
if (r.name == "mcpp-index" && r.url == kMcpplibsIndexUrl) r.name = "mcpplibs";
// ③ 内存级 artifact 填充(官方仓、未显式退出、未自定义)——Task 2 落地字段后启用
if (r.url == kMcpplibsIndexUrl && r.artifact.empty() && r.source != "git")
r.artifact = std::string(kMcpplibsIndexArtifact);
bool duplicate = std::any_of(normalized.begin(), normalized.end(),
[&](const IndexRepo& e) { return e.name == r.name && e.url == r.url; });
if (!duplicate) normalized.push_back(std::move(r));
}
cfg.indexRepos = std::move(normalized);
}config_migration.cppm 两函数各加(在现有 replace 之后):
replace_all(updated, "https://github.com/mcpp-community/mcpp-index.git",
"https://github.com/mcpplibs/mcpp-index.git");模板 :360、add_default :575 改用 kMcpplibsIndexUrl(模板是字符串字面量,直接写新 URL);publish/pipeline.cppm:158 Fork 行 → https://github.com/mcpplibs/mcpp-index;docs 4 处链接同改。
- Step 4:
mcpp build && "$MCPP_FRESH" test期望 PASS(新测试含 Task 2 字段的暂缓) - Step 5: Commit
fix(index): migrate mcpplibs index URL to mcpplibs org (#267-A)
Files:
- Modify:
src/config.cppm(IndexRepo 加字段:36;TOML 读 artifact/source:535-543;模板:359-361;add_default:571-575;write_default_xlings_json:374-396;print_env:663-668) - Modify:
src/xlings.cppm(SeedRepo struct + seed_xlings_json 签名/发射:250-253、1110-1150) - Test:
tests/unit/test_xlings.cpp、tests/unit/test_config.cpp
Interfaces (produces):
// mcpp::config
struct IndexRepo { std::string name, url, artifact, source; };
// mcpp::xlings
struct SeedRepo { std::string name, url, artifact, source; };
void seed_xlings_json(const Env&, std::span<const SeedRepo>,
std::string_view mirror = "auto", const ProjectEnv& = {});- Step 1: 失败单测(
test_xlings.cpp,import mcpp.xlings;已有则复用其 temp-dir 惯例)
TEST(XlingsSeed, PlainRepoEmissionIsByteStable) { // 零 diff 门
auto dir = make_tempdir("mcpp-seed-plain");
mcpp::xlings::Env env; env.home = dir;
std::vector<mcpp::xlings::SeedRepo> repos{ { "mcpplibs", "https://x.git", "", "" } };
mcpp::xlings::seed_xlings_json(env, repos);
std::stringstream ss; ss << std::ifstream(dir / ".xlings.json").rdbuf();
EXPECT_NE(ss.str().find(
" { \"name\": \"mcpplibs\", \"url\": \"https://x.git\" }\n"), std::string::npos);
EXPECT_EQ(ss.str().find("artifact"), std::string::npos);
std::filesystem::remove_all(dir);
}
TEST(XlingsSeed, ArtifactAndSourceEmittedWhenSet) {
auto dir = make_tempdir("mcpp-seed-art");
mcpp::xlings::Env env; env.home = dir;
std::vector<mcpp::xlings::SeedRepo> repos{
{ "mcpplibs", "https://x.git", "https://github.com/xlings-res/mcpp-index", "auto" } };
mcpp::xlings::seed_xlings_json(env, repos);
std::stringstream ss; ss << std::ifstream(dir / ".xlings.json").rdbuf();
EXPECT_NE(ss.str().find("\"url\": \"https://x.git\", \"artifact\": "
"\"https://github.com/xlings-res/mcpp-index\", \"source\": \"auto\""), std::string::npos);
std::filesystem::remove_all(dir);
}- Step 2: 编译失败确认(SeedRepo 不存在)
- Step 3: 实现——
xlings.cppm声明区加struct SeedRepo { std::string name, url, artifact, source; };,签名改std::span<const SeedRepo>;发射循环:
for (std::size_t i = 0; i < repos.size(); ++i) {
json += std::format(" {{ \"name\": \"{}\", \"url\": \"{}\"",
json_escape(repos[i].name), json_escape(repos[i].url));
if (!repos[i].artifact.empty())
json += std::format(", \"artifact\": \"{}\"", json_escape(repos[i].artifact));
if (!repos[i].source.empty())
json += std::format(", \"source\": \"{}\"", json_escape(repos[i].source));
json += std::format(" }}{}\n", i + 1 == repos.size() ? "" : ",");
}config.cppm:IndexRepo 加 artifact/source;TOML 解析读两 key(可选 string);write_default_xlings_json pairs→std::vector<mcpp::xlings::SeedRepo> 直传四字段;模板加:
[index.repos."mcpplibs"]
url = "https://github.com/mcpplibs/mcpp-index.git"
artifact = "https://github.com/xlings-res/mcpp-index"
# source = "auto" # default: artifact first, git fallback; set "git" to force gitadd_default lambda 加 artifact 形参并给 mcpplibs 传 kMcpplibsIndexArtifact;print_env 的 repo 行追加 [artifact] 标记(r.artifact 非空时);ensure_project_index_dir 的 customRepos 改 std::vector<mcpp::xlings::SeedRepo>(artifact 字段 Task 4 接入,先留空串)。canonicalize 的 ③ 分支此时编译生效。
- Step 4:
mcpp build && "$MCPP_FRESH" test全绿(含 Task 1 暂缓测试) - Step 5: Commit
feat(index): per-repo artifact source plumbing — IndexRepo/SeedRepo/toml/template (#269)
Files:
-
Modify:
src/fallback/config_migration.cppm:69-81 -
Test:
tests/unit/test_config.cpp -
Step 1: 失败单测
namespace {
std::string read_all(const std::filesystem::path& p) {
std::stringstream ss; ss << std::ifstream(p).rdbuf(); return ss.str();
}
} // namespace
TEST(ConfigIndexMigration, XlingsJsonHealsPrettyLegacyEntry) {
auto dir = make_tempdir("mcpp-migrate-xj");
auto p = dir / ".xlings.json";
{ std::ofstream os(p); os << "{\n \"index_repos\": [\n"
" { \"name\": \"mcpplibs\", \"url\": \"https://github.com/mcpp-community/mcpp-index.git\" }\n"
" ],\n \"subos\": \"default\",\n \"mirror\": \"auto\"\n}\n"; }
EXPECT_TRUE(mcpp::fallback::migrate_xlings_json_index_names(p));
auto text = read_all(p);
EXPECT_NE(text.find("\"url\": \"https://github.com/mcpplibs/mcpp-index.git\", "
"\"artifact\": \"https://github.com/xlings-res/mcpp-index\""), std::string::npos);
EXPECT_NE(text.find("\"subos\": \"default\""), std::string::npos); // 无关 key 保留
EXPECT_EQ(text.find("mcpp-community/mcpp-index"), std::string::npos);
EXPECT_FALSE(mcpp::fallback::migrate_xlings_json_index_names(p)); // 幂等
std::filesystem::remove_all(dir);
}
TEST(ConfigIndexMigration, XlingsJsonHealsCompactLegacyNameAndUrl) {
auto dir = make_tempdir("mcpp-migrate-xjc");
auto p = dir / ".xlings.json";
{ std::ofstream os(p); os << "{\"index_repos\":[{\"name\":\"mcpp-index\","
"\"url\":\"https://github.com/mcpp-community/mcpp-index.git\"}],\"mirror\":\"auto\"}"; }
EXPECT_TRUE(mcpp::fallback::migrate_xlings_json_index_names(p));
auto text = read_all(p);
EXPECT_NE(text.find("\"name\":\"mcpplibs\""), std::string::npos);
EXPECT_NE(text.find("\"url\":\"https://github.com/mcpplibs/mcpp-index.git\", "
"\"artifact\": \"https://github.com/xlings-res/mcpp-index\""), std::string::npos);
EXPECT_FALSE(mcpp::fallback::migrate_xlings_json_index_names(p));
std::filesystem::remove_all(dir);
}
TEST(ConfigIndexMigration, XlingsJsonSkipsInjectionWhenArtifactPresent) {
auto dir = make_tempdir("mcpp-migrate-xja");
auto p = dir / ".xlings.json";
std::string body = "{\n \"index_repos\": [\n"
" { \"name\": \"mcpplibs\", \"url\": \"https://github.com/mcpplibs/mcpp-index.git\", "
"\"artifact\": \"https://github.com/xlings-res/mcpp-index\" }\n ]\n}\n";
{ std::ofstream os(p); os << body; }
EXPECT_FALSE(mcpp::fallback::migrate_xlings_json_index_names(p));
EXPECT_EQ(read_all(p), body);
std::filesystem::remove_all(dir);
}- Step 2: 跑测确认 FAIL(artifact 未注入)
- Step 3: 实现(接在 name/URL 替换后):
// Artifact injection (xlings 0.4.68 per-repo artifact source). Idempotency
// gate: the res base appearing anywhere means a previous run (or a fresh
// seed) already declared it — plain replace_all would re-inject on every run.
if (updated.find("xlings-res/mcpp-index") == std::string::npos) {
constexpr std::string_view art =
", \"artifact\": \"https://github.com/xlings-res/mcpp-index\"";
for (std::string_view urlkv : {
"\"url\": \"https://github.com/mcpplibs/mcpp-index.git\"",
"\"url\":\"https://github.com/mcpplibs/mcpp-index.git\"" })
replace_all(updated, urlkv, std::string(urlkv) + std::string(art));
}- Step 4:
mcpp build && "$MCPP_FRESH" testPASS - Step 5: Commit
feat(index): heal existing registry .xlings.json — org URL + artifact injection (#267-A #269)
Files:
- Modify:
src/pm/index_spec.cppm:14-23(加字段 +artifact_applicable()) - Modify:
src/manifest/toml.cppm:1142-1150([indices] 长格式读 artifact/source) - Modify:
src/config.cppm:546-563(config.toml [indices] 同步读)+ensure_project_index_dir:680-691(SeedRepo 透传 + 不适用 warn) - Test:
tests/unit/test_manifest.cpp
Interfaces (produces):
// mcpp::pm::IndexSpec 新增
std::string artifact; // optional artifact source base (xlings 0.4.68+)
std::string source; // optional "auto" | "artifact" | "git"
bool artifact_applicable() const { // pin/local forces git — artifact only tracks latest
return !artifact.empty() && rev.empty() && tag.empty() && branch.empty() && path.empty();
}- Step 1: 失败单测(
test_manifest.cpp,仿现有 [indices] 解析测试写法;若无现成解析入口测试,则直接测 IndexSpec 语义 + toml 解析函数)
TEST(ManifestIndices, ArtifactAndSourceParsedAndPinSuppresses) {
auto m = parse_manifest_from_string(R"(
[package]
name = "p"
version = "0.1.0"
[indices]
fast = { url = "https://example.com/i.git", artifact = "https://example.com/res/i", source = "auto" }
pinned = { url = "https://example.com/i.git", artifact = "https://example.com/res/i", rev = "abc123" }
)"); // 按 test_manifest.cpp 现有 helper 名对齐
auto& fast = m.indices.at("fast");
EXPECT_EQ(fast.artifact, "https://example.com/res/i");
EXPECT_EQ(fast.source, "auto");
EXPECT_TRUE(fast.artifact_applicable());
auto& pinned = m.indices.at("pinned");
EXPECT_FALSE(pinned.artifact_applicable());
}- Step 2: FAIL 确认 → Step 3: 实现:两处解析各加两行
find("artifact")/find("source");ensure_project_index_dir远程分支:
mcpp::xlings::SeedRepo r{ name, spec.url, "", "" };
if (spec.artifact_applicable()) { r.artifact = spec.artifact; r.source = spec.source; }
else if (!spec.artifact.empty())
std::println("[warn] index '{}': artifact source ignored (rev/tag/branch/path pins force git)", name);
customRepos.push_back(std::move(r));- Step 4: 全测 PASS → Step 5: Commit
feat(index): project-level [indices] artifact/source with pin-forces-git rule (#269)
Files:
-
Test:
tests/unit/test_pm_package_fetcher.cpp:237附近克隆现有 fixture -
Step 1: 新测——entry 含 artifact/source 仍正确提取 name/url:
TEST(PackageFetcher, SeededIndexReposTolerateArtifactFields) {
auto project = make_tempdir("mcpp-pf-artifact"); // 对齐该文件现有 temp helper
auto xjson = project / ".xlings.json";
{ std::ofstream os(xjson); os << "{\n \"index_repos\": [\n"
" { \"name\": \"mcpplibs\", \"url\": \"https://github.com/mcpplibs/mcpp-index.git\", "
"\"artifact\": \"https://github.com/xlings-res/mcpp-index\", \"source\": \"auto\" }\n ]\n}\n"; }
auto repos = mcpp::pm::read_seeded_index_repos(xjson);
ASSERT_EQ(repos.size(), 1u);
EXPECT_EQ(repos[0].first, "mcpplibs");
EXPECT_EQ(repos[0].second, "https://github.com/mcpplibs/mcpp-index.git");
std::filesystem::remove_all(project);
}(返回类型按现有测试实际形态对齐 —— 以 :244 现有断言写法为准。)
- Step 2: 跑测(预期直接 PASS —— 这是回归锁,不是新功能;若 FAIL 则修
read_seeded_index_repos的对象切分) - Step 3: Commit
test(pm): lock read_seeded_index_repos tolerance to artifact/source fields
Files:
-
Modify:
.github/workflows/release.yml(:99、:284、:341-345硬编码 0.4.67 全部)、.github/workflows/cross-build-test.yml:105,218、.github/workflows/ci-linux-e2e.yml:126 -
Modify:
src/xlings.cppm:36(kXlingsVersion"0.4.51"→"0.4.68") -
Modify:
src/config.cppmprint_env(加bundled xlings pin = {kXlingsPinnedVersion}行,给常量一个消费点) -
Step 1:
grep -rn '0\.4\.67' .github/workflows/逐处替换为 0.4.68(含注释里的版本说明,注释补一句 0.4.68 = per-repo index artifact #377) -
Step 2: 常量 + print_env;
mcpp build && "$MCPP_FRESH" self env目测新行 -
Step 3: Commit
ci: bundled xlings pin 0.4.67 -> 0.4.68 (per-repo index artifact); align kXlingsVersion
Files:
-
Create:
tests/e2e/151_index_url_artifact_migration.sh(仿10_env_command.sh的隔离 MCPP_HOME 手法;需MCPP_VENDORED_XLINGS或系统 xlings,CI bootstrap 已具备) -
Step 1: 写脚本
#!/usr/bin/env bash
# #267/#269: fresh init seeds mcpplibs-org URL + artifact source; legacy
# config.toml/.xlings.json (old org URL, no artifact) are healed in place,
# idempotently, in both pretty (mcpp) and compact (xlings) JSON spacings.
set -e
TMP=$(mktemp -d); trap "rm -rf $TMP" EXIT
export MCPP_HOME="$TMP/mcpp-home"
NEW_URL='https://github.com/mcpplibs/mcpp-index.git'
OLD_URL='https://github.com/mcpp-community/mcpp-index.git'
ART='https://github.com/xlings-res/mcpp-index'
CFG="$MCPP_HOME/config.toml"; XJ="$MCPP_HOME/registry/.xlings.json"
# 1. Fresh init: new org URL + artifact in both files.
"$MCPP" self env > /dev/null
grep -q "url = \"$NEW_URL\"" "$CFG" || { echo "config.toml missing new url"; exit 1; }
grep -q "artifact = \"$ART\"" "$CFG" || { echo "config.toml missing artifact"; exit 1; }
grep -q "\"url\": \"$NEW_URL\", \"artifact\": \"$ART\"" "$XJ" || { echo "seed missing artifact"; exit 1; }
grep -q 'mcpp-community/mcpp-index' "$CFG" "$XJ" && { echo "old org leaked"; exit 1; }
# 2. Legacy heal (pretty): old URL, no artifact.
cat > "$CFG" <<EOF
[xlings]
binary = "bundled"
[index]
default = "mcpplibs"
[index.repos."mcpplibs"]
url = "$OLD_URL"
EOF
cat > "$XJ" <<EOF
{
"index_repos": [
{ "name": "mcpplibs", "url": "$OLD_URL" }
],
"subos": "default",
"lang": "en",
"mirror": "auto"
}
EOF
"$MCPP" self env > /dev/null
grep -q "url = \"$NEW_URL\"" "$CFG" || { echo "toml url not healed"; exit 1; }
grep -q "\"url\": \"$NEW_URL\", \"artifact\": \"$ART\"" "$XJ" || { echo "xj not healed"; exit 1; }
grep -q '"subos": "default"' "$XJ" || { echo "unrelated key lost"; exit 1; }
cp "$CFG" "$TMP/cfg1"; cp "$XJ" "$TMP/xj1"
"$MCPP" self env > /dev/null # idempotent second run
cmp -s "$CFG" "$TMP/cfg1" && cmp -s "$XJ" "$TMP/xj1" || { echo "not idempotent"; exit 1; }
# 3. Legacy heal (compact, old name): xlings-writer spacing.
printf '{"index_repos":[{"name":"mcpp-index","url":"%s"}],"mirror":"auto"}' "$OLD_URL" > "$XJ"
"$MCPP" self env > /dev/null
grep -q '"name":"mcpplibs"' "$XJ" || { echo "compact name not healed"; exit 1; }
grep -q "xlings-res/mcpp-index" "$XJ" || { echo "compact artifact not injected"; exit 1; }
grep -q 'mcpp-community' "$XJ" && { echo "compact old org left"; exit 1; }
echo "OK"- Step 2: 本地跑
MCPP="$MCPP_FRESH" bash tests/e2e/151_index_url_artifact_migration.sh→ OK - Step 3: Commit
test(e2e): 151 — index org URL heal + artifact seed/injection
-
mcpp.toml:3version →0.0.103;src/toolchain/fingerprint.cppm:21→0.0.103(同 commit!) - CHANGELOG.md 顶部加 0.0.103 条目(#267 URL 迁移 + 存量治愈、#269 artifact 采纳 + pin 0.4.68、[indices] artifact)
- Commit
release: 0.0.103 — index org migration + per-repo artifact adoption (#267 #269)
-
mcpp build && "$MCPP_FRESH" test全绿;e2e 相关子集(10、151)绿;可选跑本机 e2e 基线抽样 -
git push -u origin feat/index-artifact-0.0.103;gh pr create(标题feat(index): mcpplibs org migration + xlings 0.4.68 per-repo artifact adoption — 0.0.103 (#267 #269),body 引两 issue + 设计文档) - 等 CI 全绿(ci-linux、ci-linux-e2e 两 shard、cross-build-test、windows/macos e2e、fresh-install)
-
gh pr merge <n> --squash --admin(不加--delete-branch以防叠栈坑;单 PR 无叠栈,可加)
-
gh workflow run release.yml --ref main(从 mcpp.toml 派生 v0.0.103;publish-ecosystem 自动镜像 xlings-res/mcpp 双端 + 开 xim-pkgindex bump PR) - 双端 GET 核验四平台资产(
curl -sL -o /dev/null -w '%{http_code} %{size_download}',GitHub + gitcode.com 直链主机,必须 200 + 真字节;HEAD 不可信) -
gh pr merge <bump-pr> --repo openxlings/xim-pkgindex --squash --admin(publish-artifact.yml 自动发索引) - 实机验证:
xlings update && xlings install mcpp@0.0.103 -y→mcpp --version= 0.0.103 - artifact 路径专项(本 PR 主功能):fresh MCPP_HOME + xlings ≥0.4.68 捆绑,
mcpp index update→registry/data/mcpplibs变 artifact 管理(.git消失、.xlings-index-version出现);registry/.xlings.jsonmcpplibs 条目带 artifact 字段 - 收尾 pin:工作区
.xlings.jsonbootstrap pin → 0.0.103 直推 main(ci: workspace mcpp bootstrap pin -> 0.0.103 (released + indexed));ci-fresh-install.yml的 0.0.102 pin 同步 bump
- 设计文档 D1–D5 全覆盖(D1→T1,D2→T2,D3→T3,D5→T4,兼容锁→T5,D4→T6,e2e→T7);P5(CN region 对象)按设计文档明确推迟,非本计划范围。
- 类型一致性:
SeedRepo{name,url,artifact,source}贯穿 T2/T4;IndexRepo同形;artifact_applicable()只在 T4 定义与使用。 - 已知风险:test_manifest.cpp 的解析 helper 名需现场对齐(计划内已标注);
ensure_project_index_dir的spec.is_builtin()分支(mcpplibs)不进 customRepos,不受 T4 影响。