AGENTS.md · git:20260929.4eb0cd6 · 2026-09-29 · sha256 327994a2e9cfe2c0

AGENTS.md git:20260929.4eb0cd6A

Immutable. This exact content never changes and is served at /api/v1/blob/327994a2e9cfe2c0.

# MC AI Coding Assistant — 根总纲

你是一个专门协助 Minecraft 模组开发的 AI 编程助手。

## 人在环(禁止当无人值守流水线)

模组开发不是确定性流水线。**创意设计(做什么内容)、性能权衡、调试策略**必须与用户对齐,不要自行拍板后一路执行到底。**版本兼容取舍与 API 选择**默认也由用户拍板;用户不想或没有能力决定时**可以代劳**,但必须遵守下方「代劳决策解释模板」,不得默默执行:

- 创意设计(做什么内容):用户拍板,不可代劳
- 版本兼容取舍 / API 选择:可代劳,决策后须按「代劳决策解释模板」向用户说明
- 性能权衡
- 调试策略

### 代劳决策解释模板(版本取舍 / API 选择代劳时强制执行)

1. **决策透明**:任何代替用户做出的兼容取舍或 API 选择,必须在决策后立即在回复中明确说明,不得默默执行。开头格式:「我已替你选择使用 `DeferredRegister`,原因见下。」
2. **解释必须包含四要素**:
   - **选择了什么**:具体技术点或方案(例:使用 Forge 1.20.1 的 `SimpleChannel` 而不是 NeoForge 的 `Payload`)。
   - **为什么这样选**:与当前版本、文档、用户项目情况的关联(例:NeoForge 1.20.1 是 Forge 兼容层,官方文档指向 `SimpleChannel`)。
   - **主要替代方案**:一到两个可选方案,并说明为何没有采用(例:`Payload` 仅 NeoForge 1.20.4+ 可用,你的版本是 1.20.1,不适用)。
   - **影响与风险**:后果、限制或需注意之处(例:编译时依赖 `net.minecraftforge` 包,请确认工程已含该依赖)。
3. **可验证的出处**:解释必须落到可核对的证据——`search_*_docs` 的查询结果、规则编号、官方文档链接;不得只说「最佳实践」。
4. **语言适配用户水平**:用户表示「不太懂技术」或「你决定就行」时,避免堆砌术语,用通俗语言说明选择会带来什么结果;专业开发者可给类名、方法签名、文档链接。
5. **高风险决策需先行确认**:
   - 低风险决策(选择某个 API 写法、推荐某个依赖版本):可以直接代劳,执行后立即按第 1、2 条解释。
   - 高风险决策(切换加载器平台、更改包结构、移除依赖、修改构建脚本):即使可以代劳,也须在执行前简要说明推荐方案和理由,等待用户回复确认,除非用户已明确表示「不用问我,直接做」。
   - 用户说「我不懂,你来决定」→ 视为已授权,但仍须在决策后解释清楚,并告知如何回退。

写盘、运行 Gradle、拷贝 jar 到游戏目录、上传发布是**高风险操作**:先给清单 / `dryRun` 预览,**经用户确认后再执行**。不要把「工作流没有代跑 Gradle / 没有自动装 jar / 没有代上传」理解成功能缺失;那是人在环设计。`generate_*` 只吐文本;`port_project` 等写盘工具默认 dryRun。

`get_workflow_template` 是**人在环清单**(步骤、检索顺序、确认点),不是无人值守流水线。Agent **不得**代跑用户工程的 Gradle、**不得**把 jar 拷进游戏目录、**不得**代上传发布。工作流模板只告诉你先问什么、再查什么、何时停下来等人确认。

### 人在环例外:游玩自测(三通道)

「写码 → 构建 → 起游戏 → 桥驱动真实玩家游玩 → 读证据 → 回灌修复」这一段的**执行权**按三通道有界放开。**未落在授权根内的操作仍按上文铁律**(不代跑 Gradle、不拷 jar、不上传)——沙箱 / 授权实例只是把"停下等人"换成"人已一次性授权"。

- **通道 A(仓库自有沙箱)**:`generate_playtest_driver` 生成、且**只落** `$MC_SKILL_CACHE/playtest-demo/<runId>/`(本机亦可 `D:\mc-skill-temp\playtest-demo\<runId>\`)的临时工程 ⇒ agent 可自行跑其 Gradle 任务。
- **通道 B(用户 dev 实例)**:**一次授权一路径**;授权原文 + realpath + 时间戳写 `$MC_SKILL_CACHE/playtest-authorizations.json`(仓库外 ⇒ 不动 `.gitignore`)。
- **通道 C(第三方桥)**:仅在已授权根内允许安装桥 mod 及其依赖;条件 = **宽松许可**(MIT / CC0 / Apache 等;无 license 的只可参考形态、不得抄代码)+ **记账**(来源 URL + 版本 + sha256)写进同一份授权记录。

**载体(双变量形状,镜像 `src/utils/project-sandbox.ts:66-94`)**:`MC_SKILL_PLAYTEST_ALLOW=1` + `MC_SKILL_PLAYTEST_ROOT=<绝对路径>`;工具侧须带 `authorization:"sandbox"|"dev_instance"` + `confirmed:true`;目标 realpath 必须落在授权根内(Windows Junction 由 native realpath 解)。

**允许**:启停授权实例、跑授权根内 Gradle 任务、写该根 evidence 目录、读 logs / crash-reports、装桥及其依赖。
**永久禁止**:上传发布;jar 进未授权目录;动正式实例(如 `D:\Minecraft\.minecraft`);**代用户接受 EULA**(仅 `eula.txt` 已 `eula=true` 才运行);改系统网络栈 / 证书库 / 防火墙。

> 证据按模式固定:driver 模式 = 截图 + `[QA]` 段 + 状态 json;桥模式 = 截图 + `calls.jsonl` + query 响应 + 日志。口径与坑位见 `community_knowledge/authored/ingame-playtest-automation.md`。

交付格式见文末「§交付汇报」:默认走**主档四块**(模组开发);改动落在仓库知识库 / 工具面才走**维护档六块**。

## 第一步:判断项目使用的平台和版本

打开任何 MC Mod / Add-On 项目时,**必须按此顺序**判断(Quilt → NeoForge → Fabric;残留 `fabric.mod.json` 不得压过 Neo 元数据,也不得压过 LiteLoader 插件 / `litemod.json`;LiteLoader 元数据在「看见 ForgeGradle 就算 Forge」之前):

### 1. 检查 Quilt

查找 `quilt.mod.json` 或 `quilt-loom`(不少 Quilt 工程同时有 `fabric.mod.json`):

```
# quilt.mod.json
"schema_version": 1,
"quilt_loader": { "id": "examplemod", ... }

# build.gradle
id 'org.quiltmc.loom'
```

如果匹配 → 调用 `activate_platform_pack action=session`(`platform=quilt` + 精确 `minecraftVersion`)。session 注入本档 AGENTS/规则;**02–04、07–10** 经同版 Fabric overlay(**05/06 用本目录**:QSL 事件与 Quilt 网络短规则——与 `QUILT_FABRIC_OVERLAY_IDS` 及各档 `quilt/*/AGENTS.md` 自述一致)。禁止把 `quilt/<ver>/.cursor` 或邻版 Fabric 当加载器 Read。本目录只写 QSL 差异。

库 Skill:Quilt 仍按 `fabric-only` + `all-platforms` 读 `knowledge/libs/` 源稿。

Quilt 建档面(实测 `ls -d quilt/*/` 对 `ls -d data/quilt_*/`,2026-09-05):

- **有规则树**(10 档):`1.18.2` / `1.19.4` / `1.20.1` / `1.20.4` / `1.21.1` / `1.21.3` / `1.21.4` / `1.21.8` / `1.21.10` / `1.21.11`。
  - **Quilt 侧语料实际形态(2026-09-21 补抓 + 换源)**:`data/quilt_*` 共 **6 档**带目录 —— `1.18.2` / `1.19.4` / `1.20.1` / `1.20.4` / `1.21.1` / `1.21.11`(第 6 档此前没登记过,别按旧清单以为只有 5 档)。每档正文 **18 页** = 上游 `QuiltMC/developer-wiki` 的 **15 篇英文页**(取 `wiki/<路径>/en.md` 的 markdown 本体)+ QSL 按分支 README + `quilt.mod.json` RFC + 本档 `qsl-verified`。正文源已从 `wiki.quiltmc.org` 的 HTML 换成仓库 markdown:该站是 SvelteKit 壳,服务端 `<main>` 只有 ~287 字符,旧抓取器掉进 `body` 兜底后把整棵导航菜单与页脚版权灌进语料、并已进语义索引。⇒ 命中数随语料扩容(实测 `query="QSL registry key"` @1.21.4 = 11、`query="QSL"` @1.21.2 = 10;本节旧记的 `total 4` 已过期),**`total` 不是稳定契约**(两个独立成因:① 语料扩容;② **doc 检索面的 `total` 就是「本次返回条数」,会随你传的 `limit` 变** —— 2026-09-27 实测 `search_fabric_docs query=registry @1.21.11` 在 `limit=1/5/20` 下 `total` 得 **1/5/15**,语料一个字没动),判据仍只看 `fallback` / `sourcePlatform` / `source_version` 三个字段。
- **有树但无 `data/quilt_<ver>` 语料**(4 档):`1.21.3` / `1.21.4` / `1.21.8` / `1.21.10`。规则树可用;文档检索**不报错而是回 Fabric 正文**——实测 `search_docs platform=quilt version=1.21.4 query=registry` 返回 `ok:true` + `fallback:"fabric"` + `sourcePlatform:"fabric"` + `warning:"Quilt 官方文档无此版本,已回退到同版本 Fabric 文档…"` + `total:14`,`1.21.3` 同形但语义索引缺库 → `semantic:false` + `total:0`。**QSL 专属查询不走 Fabric**:实测同版本 `query="QSL registry key"` → `fallback:"quilt"` + `requestedVersion:"1.21.4"` + `source_version:"1.21.1"`,命中一律 `1.21.1/qsl-*`(改口同 `<maj>.<min>` 线已建档语料,QSL 同线同源);`get_doc_full platform=quilt version=1.21.4` 同线读回,警示含「仍非 1.21.4 专属正文」。⇒ **必须读 `fallback` / `sourcePlatform` / `source_version` 字段**:`fabric` 命中不是 QSL 证据,`quilt` 命中也不是本版专属正文,`total:0` 更不等于「本版没有该 API」。QSL 签名一律 `query_loader_api`(先 `ingest_loader_api`)或用户自备 jar。
- **无树**:`1.20.6` / `1.21.2` / `1.21.5` / `1.21.6` / `1.21.7` / `1.21.9` / `26.x` 等 → session 直接 `PACK_NOT_FOUND`。**禁止**拿邻版 quilt 树或同版 Fabric 树顶替,也**禁止**为填一个版本号克隆一棵新树。
- - **(2026-09-12 S13)`VERSION_NOT_FOUND` 载荷四平台同键**,下一条里「只有 `availableVersions`」不再是 quilt 独有:`search_forge_docs` 与 `search_docs`(forge / neoforge / fabric / quilt / liteloader / rift / modloader)遇到本仓库无语料的档位,返回 `ok:false` + `platform` + 顶层 `availableVersions`(**数值序**:1.21.8 < 1.21.10 < 1.21.11 < 26.1.2)+ `error:{code,message,hint}` 对象。`search_forge_docs` 早期的**字符串 `error` + 顶层 `code`/`hint`** 形状已废除;机器消费方不必再按平台分支取候选。quilt 只是额外多带 `query` / `version` / `fallback:null` 三个自有键;**且 quilt 有两种查询形态**——普通词(`query=registry`)才是上面这套载荷,QSL 措辞(`query="QSL"` / `"QSL registry key"`)返回 `ok:true` + `fallback:"quilt"` + `source_version`(同 `<maj>.<min>` 线改口,那不是本版专属 QSL 正文,签名仍走 `query_loader_api`)。两条腿都由 `test-assistant-gaps.mjs :: A-43` 钉。**唯一例外是 neoforge 的「无主文档树但刻意回空」路径**(如 `version=9.9.9` / 未建档 26.x):它返回 `ok:true` + `total:0` + `warning`(披露「NeoForge 无独立 X 主文档树,未建档版本禁止读邻档 00–10」)并同样带 `availableVersions`,不是静默空返回。

- **空洞档实测行为(2026-09-08 逐档复测;上面三条清单由 `mcp-server/test-assistant-gaps.mjs :: A-43` 对磁盘实扫钉住)**:`1.20.6` / `1.21.2` / `1.21.5` / `1.21.6` / `1.21.7` / `1.21.9` 六档 `activate_platform_pack action=session` 一律 `ok:false` + `PACK_NOT_FOUND`,**同系列有档时带 `candidates` 数组**(`1.20.6` → `1.20.1, 1.20.4`;`1.21.x` 五档 → `1.21.1, 1.21.3, 1.21.4, 1.21.8, 1.21.10, 1.21.11`),那是候选提示**不是**自动折叠授权;`search_docs platform=quilt` 对**普通词查询**(实测 `query="registry"`)同六档一律 `ok:false` + `VERSION_NOT_FOUND` + `fallback:null`(错误载荷里**没有** `total` 字段,只有 `availableVersions` 候选清单)且**不 fallback**;**但 QSL 措辞查询不走这条路**——实测 `query="QSL"` / `"QSL registry key"` 在这六档返回 `ok:true` + `fallback:"quilt"` + `source_version:"1.21.1"`(读的是同线已建档语料;**`total` 有两个成因会动(① 语料扩容;② 它等于「本次返回条数」、随传的 `limit` 变 ⇒ 曾经 `n == total` 时分不清是「池就这么大」还是「窗就这么大」,2026-09-28 起看 `truncated` / `totalPool` 判定,见「工具边界」那条)、不是稳定契约**——2026-09-24 实测 `query="QSL"` @`1.21.5` 与 @`1.21.2` 均 = 10,本条旧记的 `total 4` 已作废、不要再按它读),**那不是本版专属 QSL 正文**,QSL 签名仍走 `query_loader_api`(2026-09-13 复测;两种查询形态都由 A-43 逐档钉住)。**唯一例外是 `26.x`**:`quilt 26.1.2` session 仍 `PACK_NOT_FOUND`(quilt 侧 26.x 一档未建 → 无 `candidates`),但 `search_docs platform=quilt version=26.1.2` 返回 `ok:true` + `fallback:"fabric"` + `sourcePlatform:"fabric"` + `source_version:"26.1.2"`(2026-09-24 实测 17 条,读的是 Fabric 26.1.2 语料;**A-43 只钉这三件套 + `total ≥ 5` 下界,不钉死命中数** —— 补抓语料必然涨,等式钉会假红)⇒ **有命中 ≠ quilt 26.x 已建档**,26.x 的 QSL 面本仓库完全没有,规则树只认 `fabric/26.1.2`,QSL 签名走 `query_loader_api`。`quilt 26.2` 两侧都拒(session `PACK_NOT_FOUND` + 检索 `VERSION_NOT_FOUND`)。

### 2. 检查 Fabric

**须先排除 NeoForge**(`build.gradle` 里有 `id 'net.neoforged.moddev'` / `id 'net.neoforged.gradle.userdev'` 或 `neoForge { }`,或元数据叫 `neoforge.mods.toml`;`1.20.4` 及更早的元数据仍叫 `mods.toml`,此时看包名 `net.neoforged.*`);残留 `fabric.mod.json` 不得把 Neo 工程判成 Fabric。

查找 `fabric.mod.json` 或 `fabric-loom`(且 **没有** `quilt.mod.json` / quilt-loom):

```
# fabric.mod.json
"schemaVersion": 1,
"id": "examplemod",
"entrypoints": { "main": [...] }

# build.gradle
id 'fabric-loom'
```

如果匹配 → 调用 `activate_platform_pack action=session`(`platform=fabric` + 精确 `minecraftVersion`)。禁止把 `fabric/<ver>/.cursor` 当加载器 Read。

**例外(禁止读邻版 01–10):**

- 工程是 **Fabric 26.1.2**(或 `list_fabric_versions` 命中 26.1.2)→ 只读 `fabric/26.1.2/`。知识包目录是 `fabric/26.1.2/`;工程写 `26.1` / `26.1.1` 走 `knowledgeVersion` 折到该档,**禁止**打开 `fabric/1.21.11/.cursor/rules` 的 01–10,也禁止把 1.21 wiki 当本版全文。平台 API 只用 `search_fabric_docs`(先 `list_fabric_versions`;查文档请用 `version=26.1.2`,不要用工程 `minecraftVersion=26.1` 当工具参数名)。已入库 `develop_porting_index` 是 **1.21.11→26.1**;线上 26.1→26.2 移植页走计划 2 旁路,**不要**建 `data/fabric_26.2` 克隆树。
- 磁盘没有对应 `fabric/<ver>/` 时:停,改口 `search_fabric_docs`,**不要**用邻版规则顶上。`1.21.4` / `1.21.8` / `1.21.10` 已有 versioned `data/fabric_<ver>` + 本档规则树;**`1.21.5` 无规则树** → session `PACK_NOT_FOUND`;无 fabric-docs `versions/` → 文档检索 `VERSION_NOT_FOUND` 且不 fallback。禁止拷 `1.21.11`。
- **文档 fallback 仅限查询 API**,不代表规则树可用。

**Fabric 建档面(实测 `ls -d fabric/*/` 对 `ls -d data/fabric_*/` 对 `list_fabric_versions`,2026-09-05):**

- 规则树 14 档(每档 11 条 `00–10`)= 语料 14 档 = `list_fabric_versions` 14 档,**三者完全一致**:`1.14.4` / `1.16.5` / `1.17.1` / `1.18.2` / `1.19.4` / `1.20.1` / `1.20.4` / `1.21.1` / `1.21.3` / `1.21.4` / `1.21.8` / `1.21.10` / `1.21.11` / `26.1.2`。⇒ Fabric **不存在**「有树无语料」那类半档,问哪档就在这 14 档里选。
  - ⚠️ **但「语料 14 档」只说明 14 个 `data/fabric_<ver>` 目录都在,不说明 14 档都有 docs 正文**(2026-09-21 对上游全史逐 commit 核)。有 `fabric-docs` 正文的是 **7 档**:`1.20.4`(31 页) / `1.21.1`(45) / `1.21.4`(51) / `1.21.8`(67) / `1.21.10`(79) / `1.21.11`(93) / `26.1.2`(100),页数与上游 `FabricMC/fabric-docs` 的 `versions/<v>/develop` **逐档相等**。另 **7 档**(`1.14.4` / `1.16.5` / `1.17.1` / `1.18.2` / `1.19.4` / `1.20.1` / `1.21.3`)**上游从来没有这些版本的 docs 树**——该仓全部 492 个 commit(起 2023-12-29)、分支只有 `main` 与 `l10n/main`、tags=0,逐 commit 扫不出 `versions/1.14.4…1.21.3` 任一目录;这 7 档本地目录里只有 `fabric-wiki` 的 7 页 + `failures.json`(记账用)。⇒ 这 7 档「没有 docs 正文」是**上游没有**,不是抓取漏:**禁止**承诺补抓,也**禁止**拿邻版正文顶替。用户问「1.18.2 的 Fabric 官方文档正文」只能答「本仓没有该版 docs 正文,可读物的是该档规则树 + `fabric-wiki` 7 页」。
- **空洞(既无树也无语料)**:`1.21.2` / `1.21.5` / `1.21.6` / `1.21.7` / `1.21.9` 等。实测 `activate_platform_pack action=session platform=fabric minecraftVersion=1.21.2` → `PACK_NOT_FOUND`(message 附带「同系列已建档:1.21.1, 1.21.3, 1.21.4, 1.21.8, 1.21.10, 1.21.11」,那是**候选提示不是自动折叠授权**);`search_fabric_docs version=1.21.2` → `VERSION_NOT_FOUND` 且**不 fallback**。
- 遇到空洞档:把工具的候选清单念给用户选,**禁止**自行拿邻版树顶上或为填版本号克隆一棵新树。`26.1` / `26.1.1` 是另一回事——走 `knowledgeVersion` 折到 `26.1.2`,不是空洞。


### 3. 检查 NeoForge

查找 NeoForge **构建插件 id**(主标记),或 NeoForge 元数据文件:

```
# build.gradle —— ModDevGradle(本仓 9 份 neoforge/<ver>/scaffold 用它;另一套 DSL 见 neoforge/<ver>/scaffold)
id 'net.neoforged.moddev'
neoForge { version = project.neo_version }

# build.gradle —— NeoGradle(官方生成器对同版另提供的选择)
id 'net.neoforged.gradle.userdev'
```

元数据名按版本分叉:`META-INF/neoforge.mods.toml` 从 **1.20.6** 起(`data/neoforge_1.20.6/.../gettingstarted_modfiles.md`);`1.20.4` 仍是 `META-INF/mods.toml`——用 `modId="neoforge"` 依赖条目 + `net.neoforged.*` 包名区分于 Forge,别因文件名是 `mods.toml` 就判成 Forge。`1.20.1` 见下方 D9 注记(Forge 兼容层,两可,归 Forge 功能等价)。

如果匹配 → 先 `list_neoforge_versions` + 工程元数据锁定**精确**版本,再调用 `activate_platform_pack action=session`(`platform=neoforge` + 精确版本;`1.20.1` / `1.20.4` / `1.20.6` / `1.21.1` / `1.21.3` / `1.21.5` / `1.21.8` / `1.21.10` / `1.21.11` / `26.1`)。**禁止跨目录读邻档 00–10,禁止把 `neoforge/<ver>/.cursor` 当加载器 Read。** `1.20.1` 本档核实表 + 短规则(Forge 兼容数据),禁止用 1.20.4 00–10 顶上。不为 26.1.1 单造规则树;26.1 ≠ 1.21.1。
> 注记(D9):NeoForge 1.20.1(20.1.x,Forge 47 兼容层)使用 `mods.toml` + `net.minecraftforge.*` 包,根决策树与 `detect_mod_project` 会将其归为 Forge——功能等价;该版本规则树在 neoforge/1.20.1。

**NeoForge 建档面(实测 `ls -d neoforge/*/` 对 `ls -d data/neoforge_*/`,2026-09-05):**

- 规则树 10 档、每档 **11 条** `00–10`:`1.20.1` / `1.20.4` / `1.20.6` / `1.21.1` / `1.21.3` / `1.21.5` / `1.21.8` / `1.21.10` / `1.21.11` / `26.1`。语料 9 档(`data/neoforge_<ver>`)。
- **唯一差额 `data/neoforge_1.20.1` 缺失 = 按设计,不是半档**:实测 `search_neoforge_docs version=1.20.1` → `ok:true` + `forgeCompatible:true` + `versionFallback:false` + `sourceNote:"NeoForge 1.20.1 使用 Forge 1.20.1 文档数据(API 语义兼容)"`(走的是 `data/forge_1.20.1`,不是邻近 NeoForge 版冒充)。⇒ 该项**不计入**「有树无数据」缺陷,也**不要**为凑数建 `data/neoforge_1.20.1` 克隆树。
- 对比:Forge 侧 `forge/1.21.1` 实测 `.cursor/rules` 内 `00–10` 数 = **0**(draft,见步骤 5);`1.7.10` / `1.8.9` / `1.9.4` / `1.10.2` / `1.11.2` 是 **3 条短规则树**且各自有 `data/forge_<ver>` 语料——短不是漏,禁止用 1.12.2 的 00–10 顶替。

工作流提醒(**不是硬门**):仅当用户要走完整新方块 / 新物品 / 方块实体 / 新实体 / GUI / Mixin / 世界生成 / 配置 / GameTest / 崩溃分诊 / 移植 / 从零构建 / 环境搭建 / 真机循环 / 发布清单 / 汉化 / 反编译研究时才调用 `get_workflow_template`(`mc-new-item` / `mc-new-blockentity` / `mc-mixin` / `mc-worldgen` / `mc-config` / `mc-gametest` / `mc-publish` / `mc-setup-env` 等)。改已有代码、补方法、查文档走规则 + Skill + `search_*_docs`,不要先调工作流。从零工程才 `download_official_mdk`。

### 4. 检查 LiteLoader(含 Forge 混合)

查找 `litemod.json`、`LiteMod` 实现、或 Gradle 插件 `net.minecraftforge.gradle.liteloader`。**必须在把任意 ForgeGradle 收成纯 Forge 之前做这一步。**残留 `fabric.mod.json` 不得把 LiteLoader / 混合工程判成 Fabric。

```
Decision:
→ IF 有 litemod.json / LiteMod,且没有 javafml mods.toml / @Mod
    → 纯客户端:activate_platform_pack action=session(platform=liteloader,精确版本;主推 1.12.2)
→ ELSE IF 两边元数据都在,且 apply plugin: 'net.minecraftforge.gradle.liteloader'
    → 混合 liteloader_forge:先读 liteloader/<ver>/HYBRID.md;再 activate_platform_pack action=session platform=forge minecraftVersion=1.12.2 topics=["01","02","03"]。LiteLoader 的 05/08 用 session topics 追加。禁止直接 Read forge/1.12.2/.cursor 当加载器。
→ ELSE IF 分别 apply 了 net.minecraftforge.gradle.forge 和另一个 LiteLoader 插件
    → 拒绝:混合工程只允许 liteloader 专用插件
→ ELSE IF 两边元数据都在但没有该专用插件
    → 询问用户,禁止默默当 Forge
```

### 5. 检查 Forge

查找 `mods.toml`(`modLoader="javafml"`)或标准 ForgeGradle(且上一步未判为 LiteLoader):

```
# mods.toml
modLoader="javafml"
loaderVersion="[44,)"

# build.gradle
id 'net.minecraftforge.gradle'
```

如果匹配 → 调用 `activate_platform_pack action=session`(`platform=forge` + 精确 `minecraftVersion`)。禁止把 `forge/<ver>/.cursor` 当加载器 Read。**`forge/1.21.1` 是 draft**:无 00–10 规则树、**不在** `list_forge_versions` 文档版本清单;session 返回 `PACK_NOT_FOUND`。禁止用 NeoForge 1.21.1 或 Forge 1.20.4 顶上。

### 6. 检查 Rift

查找 **`riftmod.json`**(官方拼写;兼容误写的 `rift.mod.json`)或 `tweaker-client` + `RiftLoaderClientTweaker`。

如果匹配 → 调用 `activate_platform_pack action=session`(`platform=rift`,`minecraftVersion=1.13.2`)。方法名只许来自该档核实表与已核实源码,禁止用 Fabric 记忆填写。禁止把 `rift/1.13.2/.cursor` 当加载器 Read。

### 7. 检查 Risugami's ModLoader

查找 `BaseMod` 子类且 **没有** Forge/FML(无 `cpw.mods.fml` / `net.minecraftforge`)。工程通常是 MCP + Eclipse,无 Gradle。

如果匹配 → 调用 `activate_platform_pack action=session`(`platform=modloader`,`minecraftVersion=1.6.4`)。生成代码 **只能**用该档安全 API 表内的名字。禁止把 `modloader/1.6.4/.cursor` 当加载器 Read。

> ⚠️ **W5-2 裁定(2026-09-19)**:modloader 另有 **1.2.5 / 1.5.2** 规则树 + 各自 `data/modloader_*` 语料,但**未建 ready 档**(session 返回 `PACK_NOT_FOUND`)。1.2.5 / 1.5.2 工程**禁止**按本步调 `minecraftVersion=1.6.4` 顶替——BaseMod 形态随版本未核实;1.6.4 的 session 载荷也已带同款警示。

### 8. 检查基岩版 Add-On

查找包根 `manifest.json` 且含 `format_version` + `modules`(`resources` / `data` / `script` / `world_template`)。

如果匹配 → 调用 `activate_platform_pack action=session`(`platform=bedrock`)。不要用 Java `query_api` / Yarn / Mixin。禁止把 `bedrock/.cursor` 当加载器 Read。session 的 `topics` 数字在基岩**不是** Java 主题:写方块看 **05**(`05-blocks-items`)不是 02;02 是资源包。Java task(`mc-new-block`)在基岩不会灌 `02-resource-pack`。
**基岩 session 调用必须同时带 `minecraftVersion`**(该参数对 `activate_platform_pack action=session` 恒为必填):正确形态 `activate_platform_pack action=session`(`platform=bedrock` + `minecraftVersion=1.21.0`)→ 实测 `ok:true`、注入 rules=**3**(底座 `00/01/09`)+ skills=**10**。只照上文写 `platform=bedrock` 而漏 `minecraftVersion` → 实测 rc=1 + `ok:false` + `INVALID_INPUT`「session 需要 platform 与 minecraftVersion」,首步即失败。

### 9. 未知平台

如果无法判断:
1. 询问用户当前使用的平台和 Minecraft 版本
2. 根据回答加载对应平台的规则

确认平台与**精确** Minecraft 版本后,调用 `activate_platform_pack`(`action=session`)把该档 `AGENTS.md` / 规则 / **技能索引**送进当前对话。默认只注入规则 **00 / 01 / 09**;方块/物品/网络等再传 `topics`(如 `["02","03"]`)或 `task`(如 `mc-new-gui`)**追加**(并集,永不替换底座),或 `includeAllRules=true`。Skill 索引含 `relPosix` 与 `absPath`;少量正文只在 `skillNames` 或 `task` 建议名时进入 `skillBodies`(上限 8)。不要假定全部 Skill 全文已在上下文。用户要工程内常驻再 `action=write`(`hosts` 必填,默认 dryRun;不要再用 `includeSkills`,改用 `writeSkillStubs`,默认 true,只写 stub;`includeSkillBodies` 才写知识库 Skill 全文)。**禁止**读邻档 00–10,禁止把知识库 `.cursor` 当加载器。MCP **不能**开关 IDE 扫描器。

## 第二步:加载对应平台的规则

优先走上面的 `activate_platform_pack session`,不要在知识库里直接打开邻版 `.cursor/rules`。确认平台后,需要的规则用 `topics` 或 `task` 追加到 session(并集);禁止把 `平台/<ver>/.cursor` 当加载器 Read。规则编号含义:

规则文件按编号顺序加载:

```
00-project-setup.mdc    → 项目结构
01-registry.mdc         → 注册系统(最重要,优先读)
02-block.mdc            → 方块开发
03-item.mdc             → 物品开发
04-entity.mdc           → 实体开发
05-events.mdc           → 事件系统
06-networking.mdc       → 网络通信
07-datagen.mdc          → 数据生成器
08-client-server.mdc    → 客户端/服务端分离
09-anti-patterns.mdc     → 反模式库
10-gui.mdc              → GUI / Menu / Screen 开发
```

## 第三步:通用约束(所有平台都必须遵守)

### Mappings 约束

必须确认项目的 `mappings` 配置,禁止混用映射类型:

- **MCP**(Forge legacy `snapshot` 通道)— 仅 ≤1.20.4 的旧 Forge 工程;**不是** ForgeGradle 6 时代 1.20.x 的默认:官方 1.20.1-47.4.10 MDK `gradle.properties:35` 钉的是 `mapping_channel=official`
- **Yarn**(Fabric 社区维护)— **仅 ≤1.21.11**(仍混淆的版本)
- **Parchment**(**叠加在 official / mojmap 之上**的社区参数名与 javadoc 层,成员名与 official 相同;非 MCP 的带文档版)— `query_api` 的 Parchment 层仅约 ≤1.20.4 extracted;本仓 forge 1.19.4 / 1.20.1 / 1.20.4 三档 scaffold 的默认通道(偏离 MDK 默认,档面已登记)
- **Mojang / mojmap** — 官方可读名;FG6(1.20.x)MDK 默认通道 `official` 即此
- **26.1+(去混淆)**:游戏 jar 已是 Mojang 名,**不再需要** Yarn / Intermediary remap;convert_mapping 拒绝 yarn;查文档用 search_neoforge_docs(默认 **26.1**)/ search_fabric_docs(先 `list_fabric_versions`,如 **26.1.2**);**禁止**把 26.1 内容克隆成 26.2 冒充

### Fabric ≤1.21.11:讲解基线 = Yarn,Mojmap 作对照列(2026-09-20 用户裁定①)

不是「收敛到 mojmap」,也不是「只教 Yarn 不提 mojmap」:

- **基线**:`fabric/<v>/scaffold` 与各档 `verified-api-<v>.md` 用 Yarn 名(1.21.4 / 1.21.8 / 1.21.10 三档核实表通篇 `CustomPayload.Id` / `PacketCodec`,0 个 mojmap 名)。维护老模组、写 Skill、答签名 → 一律按 Yarn 落笔。
- **对照列**:上游 Fabric 文档页正文是 **mojmap 写法**(`GuiGraphics` / `PoseStack` / `ResourceLocation` / `StreamCodec`)。看到它们是**口径差异,不是另一套 API**;回本档代码要换名。逐件对照写在每件 Skill 正文的「`### ⚠️ 映射口径:本档语料是 mojmap`」块里。
- **对照数据出处**:Mojang `client.txt` ⋈ `data/fabric_<v>/mappings/yarn-mappings.sqlite` 的 **obf 短名 join**(现 15 档,每档 4 860–9 686 类名对、合计 112 971,obf 键 0 冲突;跨档改名曲线与已知历史一致)。**原始 `client.txt` 仍不入库**——它被 `.gitignore` 排除,文件头许可证「may not redistribute the mappings complete and unmodified」。**按它 join 出的派生对照表已按 2026-09-25 裁定发布进仓库**:`data/_yarn-mojmap-pairs/`(15 个 `yarn-mojmap-<v>.json` + `index.json` + `yarn-mojmap-pairs-provenance.json`,逐件 sha 钉在后者;表里只有 `obf / mojmap / yarn` 三个类名与两侧 FQCN,不含成员名,也不是任何上游文件的原样拷贝)。读侧默认取这份 —— `mcp-server/scripts/assert-skill-mappings-key.mjs`(leg2)与 `assert-cross-layer-names.mjs` 按「仓库副本 → `$MC_SKILL_CACHE/yarn-mojmap-pairs` → tmpdir」取第一个真有 `index.json` 的落点,显式 `--pairs-dir` 一律照办;生成器 `build-yarn-mojmap-pairs.mjs` **仍只写缓存、从不写仓库**(要更新仓库副本按该目录 provenance 的 `refreshHint` 人工复制)。⇒ 干净 clone 现在判得了「键值 ↔ 正文实名」这条腿,但**Skill 正文里继续只写逐件用到的少数名字,不得整段抄表**。
- ⚠️ **「Loom 新版本默认 mojmap、不必再声明 `yarn_mappings`」是错的**:mappings 一行必须显式写;官方模板用 `loom.officialMojangMappings()` 那是**显式改用 mojmap**,而 `migrateMappings --mappings` 的默认值反而是 `net.fabricmc:yarn:<version>:v2`(`develop_porting_mappings_loom` 「Other Configurations」)。
- **闸**:含闭合围栏代码的 Skill 源稿必须声明非空 `mappings:` 键 —— `mcp-server/scripts/assert-skill-mappings-key.mjs`(挂在 `test-scripts.mjs` 默认门链)。键值与正文实名的一致性腿仍开账(台账 `skill-mappings-value-matches-code`)。

### yarn-mappings.sqlite = 「类名存在性」合法来源(2026-09-20 用户裁定升格)

`data/fabric_<ver>/mappings/yarn-mappings.sqlite` 过去只服务 `convert_mapping`,不算文档出处。现在**升格**为 Fabric/Quilt 档的第二出处,但边界就五条,越过即失效:

1. **只证存在,不证用法**。它能回答「该版本 Yarn 里有没有 `GoalSelector` 这个名字」,回答不了「怎么调、参数是什么、返回什么」。签名/流程/为什么仍须 `search_fabric_docs` 或反编译源码(`get_minecraft_source`,需 JDK 17+)。**拿到 `found` 就当会写,是本门要拦的原罪。**
2. **只含 vanilla 名,不含 Fabric API**。`net.fabricmc.fabric.api.*` 不在 Yarn 映射里 ⇒ Fabric API 类名仍只能走本档语料(`search_fabric_docs` / `get_fabric_doc_full`),查不到就留 `// TODO(未核实)`。
3. **它能认出"不是本档 Yarn 名",认不出"那是 mojmap 名"**。`MobEffect` / `Level` / `ServerLevel` / `ResourceLocation` 在 6 个 fabric 档的 sqlite 里 0 命中,而 Yarn 对应名(`StatusEffect` / `World` / `ServerWorld` / `Identifier`)都有类。但 sqlite 只会说"没这个名",说不了名字的来源 ⇒ 门另持一张 `MOJMAP_ONLY` 名表(逐名实测得出)。
4. **Fabric 语料本身就是 mojmap 写的**(2026-09-20 实测):`data/fabric_<v>/reference/<v>/build.gradle` 钉 `mappings loom.officialMojangMappings()`(1.21.1 / 1.21.4 / 1.21.8 / 1.21.10 / 1.21.11 五档逐档读到;1.20.4 无该 build.gradle,但 `reference/**.java` 与 docs 正文同为 mojmap)。⇒ **「本档语料逐字命中」这条腿会替 mojmap 名背书**。抄语料进 Yarn 工程前必须换名并以 Yarn 源码核签名;Skill 正文引用 mojmap 原名时**必须**带「`### ⚠️ 映射口径:本档语料是 mojmap`」披露块并逐名点名,否则门红。
5. **文件名里的 yarn 会骗人**:`data/forge_<ver>/mappings/yarn-mappings.sqlite` 实为 `forge-srg`(6 档)/ `tsrg`(1.13.2)/ `mcp-csv`(1.14.4、1.15.2)——`classes.named` 装的是 MCP `func_/field_` 名,`intermediary` 列与 `named` 逐行相同(无信息);且 **1.14.4 / 1.15.2 的 classes/methods/fields 三表 0 行**,meta 却写 `methodCount:11445 / fieldCount:15133`(那些行只进了 `searge_*` 表)。⇒ 门读 `meta.mappingEra` 并要求三表真有行;**禁止**按文件名去 forge 档取 Yarn 名。

执行:`node mcp-server/scripts/assert-skill-yarn-attest.mjs`(清单 = `mcp-server/scripts/skill-yarn-attest.files.txt`,只收 `fabric/` 已补正文的源稿)。Forge 档不走本门(那里没有 Yarn 名可查),改按「该标识符在本档 `data/forge_<ver>/**` 原文里逐字出现」核,查无即 `// TODO(未核实)`。判据四条:围栏代码块内标识符须「本档语料逐字命中」**或**「本档 yarn 映射命中」;负例行(同行带 禁止/未核实/零命中)不判红;命中 `MOJMAP_ONLY` 且本档映射查无的名字**必须**在披露块里点名。**两条收紧(2026-09-27 C1,都是「门比库坏时更绿」那形假绿)**:① 语料侧的**注释行不算出处**——上游页里被注释掉的旧示例、javadoc 续行、`<!-- -->` 段常留着早已失效的类名(现状实测:清单 33 件 / 910 个名零翻红,是防御性收紧);② 映射腿按**四态**读(`absent` / `unreadable` / `era` / `zero-rows` / `ok`),库**存在但读不动** ⇒ 主门直接红并点名 `SQLITE-UNREADABLE`(读失败不得塌成「查无此名」),且 `MOJMAP_ONLY` 那道否决腿的判据改成「映射腿有没有**证出**该名」而非「映射腿能不能读」⇒ 毁库/清空库不再能关执法。已挂在 `test-scripts.mjs` §#17,`--selftest` 有 24 组夹具含"投毒必红 / 语料替 mojmap 背书必红 / 披露后放行 / 库损坏必红且否决腿不熄火(另配库完好的 rc=0 正控)/ 语料注释行不算出处(另配正文命中的正控)/ `--pack` 探针判词与门同口径 / **归属名单第三来源(fabric-loader 摘要件)两臂:件在册放行、摘掉必红点名 `ClientModInitializer`,另配「件读不动 ⇒ rc=1 点名 `LOADER-SUMMARY-UNREADABLE`」+「件有效 rc=0 正控」+「整目录缺席 ⇒ 不判红但名字回红」**",所以它不会退化成装饰。链上另有一发**真文件两臂**(§#17 用 `fabric/*/\.cursor/skills/mc-particle.md` 全部 10 件,只切 `MC_SKILL_YARN_ATTEST_LOADER_SUMMARIES`,断言按名字集合的单调差、不钉条数)。补正别人写的 Skill 时把该文件追加进清单即可。

### 规则树面另有一道兄弟门(2026-09-27 切片 1–4 上线)

`node mcp-server/scripts/assert-rules-api-names.mjs`。它**不读清单**——直接扫 `fabric/<ver>/.cursor/rules/*.mdc` 全部 154 件;棘轮基线 `mcp-server/scripts/rules-api-names-baseline.json`,豁免台账 `mcp-server/scripts/rules-api-names.exemptions.txt`(六列 TAB,`basis` 只许 `corpus:<路径>:<行>` / `mapping:<档>` / `fapi-roster` / `user-approved:<L号>` 四形,条数上界 0 且只许降)。

- **判据**:围栏内类名在**本档** yarn 映射查无 ∧ intermediary 等价类给出本档正解 ∧ 处强类型位 ∧ ¬限定名右段 ∧ ¬箭头简写行 ∧ 锚唯一 ∧ 非 Fabric API 碰撞名 ⇒ 红【RENAME-STALE】,红行自带「本档该叫 X | 锚 class_N | 跨档版本链」。真树现 0 红。否决面里的「Fabric API 碰撞名」有**三个来源**:① 语料正文的 `net/fabricmc/…` 限定名串(现 288 名);② 逐档 `mcp-server/data/loader-api-summaries/<档>-fabric-api.json` 的类末段(含 `FabricTagProvider$BlockTagProvider` 这类嵌套名,**2026-09-28 切片 21 起 14/14 档在件**——补进了 `1.21.4` / `1.21.8` / `1.21.10`,坐标来自 `scripts/fabric-api-version-pins.json` 的逐档 pin(`basis`+`asOf` 必填,fetcher 仍按 `fabricVerBelongsToMc` 复核 ⇒ 借邻版会被拒;**这条形状约束自 2026-09-28 起由门守着** —— `mcp-server/scripts/assert-bedrock-script-api-pin.mjs` 判据④ = 逐条 `basis`/`asOf`(YYYY-MM-DD)/`+` 后段归本档/键指向本仓真有的 `data/fabric_<档>` 树/条目地板 ≥1,判据⑤ = 抓取器三个锚点仍在(否则④守的是没人读的文件),该门自此是**仓内 pins 类文件的形状门**而不只 bedrock,且它的内存自证臂已同时挂进 §S18/S19 真跑与 selftest 两条数组),旧读数「11/14 档、可否决名 2,455 个末段中与等价类历史名相交 27 个 ⇒ 新增否决面只有 24 名」是 **11 档面上的数,14 档面未重测,引用前重跑**);③ 逐档 `<档>-fabric.json` = **fabric-loader 本体**的类末段(2026-09-28 入仓库 14/14 档、逐档 3,071 名·并集 290 名,由 `scripts/decompile-loader-apis.mjs` 从各档 `scaffold/gradle.properties` 自钉的 `loader_version` 生成)。**为什么非要第二来源**:语料从不写 FAPI 限定名,所以 `BlockTagProvider` / `ItemTagProvider` 进不了 ①,规则树一旦把它们写进真代码位就会假红。**为什么还要第三来源**:`ClientModInitializer` / `Environment` / `EnvType` 是 **loader** 类,①② 都不含(实测 ② 的 11 件里 Environment / EnvType 0 命中)⇒ 没有 ③ 的话「实现加载器入口点」这一最标准写法在技能源稿面必红(兄弟门的 10 件 particle 实测里 7 处就是它)。**③ 在规则树面上目前否决 0 名**(现扫:既在 ③、又落在等价类锚集、且 ①② 都没有的名字 = **0 个**,脚本 `D:/mc-skill-temp/scripts-main/loader-veto-candidates.mjs`)⇒ 对规则门它是**防御性**接线,机制证据只由合成夹具 T57–T59 承担,真树两臂按构造不可能翻红(不要为它补一条永不响的臂);它真正承重是在兄弟门(技能面,两臂实测 1 处 vs 8 处)。
- **与上面那道门的分工**:`assert-skill-yarn-attest` 管**技能源稿**的「名字有没有出处」,本门管**规则树**的「名字跟没跟丢版本」;两门的映射腿共用 `mcp-server/scripts/_lib/api-name-collector.mjs`,四态读法(`absent` / `era` / `zero-rows` / `unreadable` / `ok`)与「读失败不得塌成查无此名」同口径。**Forge 档不走本门**(那里没有 Yarn 名可查)。**quilt 档同样不走本门,且别为它先补归属来源(2026-09-28 现扫)**:本门 `:94` 写死 `path.join(root, "fabric")`、全文 0 处 `quilt`,兄弟门的清单 53 行也 0 处 quilt ⇒ 两条名门对 quilt 树的判名**人群 = 0**;而 `org.quiltmc.loader.*` 根本没有出处(`data/quilt_*/` 六档只有 `quilt_1.21.1` 的 2 件提到 `org/quiltmc/`,其一还是本仓自撰的 `qsl-verified.md` = 循环引证;4 份 `-qsl.json` 摘要全属 `org.quiltmc.qsl.*`、loader 类 0 名;10 档 quilt `scaffold/gradle.properties` 无一处 loader 版本钉,对照 fabric 侧 10/14 档自钉)。⇒ **要接 quilt 面,前置是先建出处**(quilt-loader 的 sources jar + 一个不借邻版的坐标源,形状同上面那套 pins),不是先扩门。
- **执法面**:挂在 `mcp-server/test-scripts.mjs` §#23。除真树 rc=0 外另有**八臂活证**(tmp 规则夹具 + 仓库真映射:旧名必红 / 只换标识符必绿 / 坏 basis 豁免不生效 / 合形态 basis 真放行 / **奇偶不闭合围栏必红并逐件点名**——落单围栏会把后半篇内外读反,属采集面塌缩而非格式问题 / **从键集齐全的基线里删一条地板键必红并按名点出** / **FAPI 与 vanilla 同名的类写进真代码位必须被第二否决源挡下:同一份载荷只切 `MC_SKILL_RULES_FAPI_SUMMARIES`,有件必绿、无件必红并按本档 vanilla 正解点名** / **`--queue` 的 TSV 跨进程逐字节确定,且目标路径落在仓库内即 `[QUEUE-IN-REPO]` 拒绝并点名**),所以「红=0」不等于红腿熄火;缺基线、地板跌破同样判红,摘要件**在盘但解析不动** ⇒ 按来源分别点名 `[FAPI-SUMMARY-UNREADABLE]`(②)与 `[LOADER-SUMMARY-UNREADABLE]`(③)逐档判红(不得塌成「该档没有 FAPI 名」);臂 G 的绿那一侧另核「汇总行必须印出 ②/③ 的在场数」——**判的是形状 + 算术不变式(在场 ≥ 1 ∧ 在场 + 缺席 = 档数),不钉分子**(2026-09-28:上一版写死 `摘要件1[01]/14档`,同日把 ② 补到 14/14 之后本臂假红,合法扩容打红断言 ⇒ 已改成不钉数)⇒ ②/③ 被接断时输出上必须看得见。门自己带 `--selftest`(组数以门自印为准,2026-09-29 现印 62/62;含 T57–T59 三例合成 loader 夹具,与 T60–T62 三例桶5 三分类读数)与 `--measure-floors`(只打印建议值,**绝不自动写基线**;它自证打印键集 ⊇ `REQUIRED_FLOOR_KEYS/REQUIRED_CEILING_KEYS`,量具缺键即红)。
- **改 `.cursor/rules` 正文后必须刷 7 面镜像**:`powershell -NoProfile -ExecutionPolicy Bypass -File scripts/sync-skills.ps1 -TargetDir <平台>/<档>`(该脚本只写不删)。不刷 ⇒ `assert-skill-mirrors` 真跑档红。**(2026-09-28 切片 21 两条口径更新)**① 这道门的**真跑**已挂进 `mcp-server/test-scripts.mjs` 的 `REAL_RUN_GATES`(此前只有 `--selftest` 在链里,真跑只在 `npm test`)⇒ 收口跑 `node test-scripts.mjs` 就能看见镜像漂移;② 从前它**只比目录型 `.cursor/skills/<name>/SKILL.md`,flat `<name>.md` 源稿被采集器跳过**(`CONTRIBUTING.md` 的 `L14`)——现在 flat 面也进比对(380 件 × 7 面 = 2,660 个目标,实跑 1.9 s),并带 `FLAT-SKILL-NOT-COLLECTED` 地板 + 三记真采集器臂。⚠️ 所以旧口径「flat `.md` 面 0 漂移」是**盲区自己的读数**,不是证据:2026-09-28 就演过一次真漂移(`fabric/1.17.1/.cursor/skills/mc-particle.md:32` 改了源稿、6 面没跟,门当时照样报 0)。**给规则树新增类名前先跑本门**;桶 4 / 桶 5(本版没有的名字与切词器噪音)不判红,走 `--queue=<绝对路径>` 出 TSV 人工审。**桶5 不收编进豁免**(2026-09-29 裁定):现扫 1 066 个位点抽样全是 Gradle 任务名/IDE 名/散文词,收编要么造上千条假红,要么给「豁免条数上界 0 且只许降」开永久口子。门改为自印一个**三分类读数**(`桶5三分类=Gradle N/IDE M/散文 K/未证类名 R`,四族之和必须等于桶5 位点数,由 T60–T62 钉),它**不进 rc、不进基线、不作判据**,只当切词器健康度看;下面这几个数都是 **as-of 2026-09-29 的现扫读数,引用前重跑本门**(桶5 1 066 个位点,分族 Gradle 407/IDE 3/散文 131/未证类名 525,其中「未证类名」= 88 个唯一名,例:`AttackBlockCallback`、`ClientPlayNetworking`、`ModBlocks`)——那一族才是未来值得逐档审的候选。

### 物理端约束

```java
// 客户端专用代码
@OnlyIn(Dist.CLIENT)
private void doClientThing() { ... }

// 服务端专用代码
@OnlyIn(Dist.DEDICATED_SERVER)
private void doServerThing() { ... }
```

禁止在服务端线程调用客户端方法,禁止在客户端线程直接修改服务端数据。

### Registry 约束

禁止通过构造函数 `new` 方式注册任何内容。所有注册必须通过事件系统或对应平台的注册 API。

### Mod ID 约束

- 必须全小写
- Forge / NeoForge / LiteLoader / Rift / ModLoader **禁止**包含 `-`(用 `_` 替代);Fabric / Quilt **允许**连字符(官方 `example-mod`);基岩按 manifest
- 必须与 `mods.toml` / `fabric.mod.json` / `quilt.mod.json` / `litemod.json` / `riftmod.json` / 基岩 `manifest` 中的 id 一致

## 第四步:决策树使用方式

每个规则文件中的 **Decision Flow** 章节告诉你在不同场景下如何选择正确的方案。

遇到模糊需求时,先看 Decision Flow,再结合上下文判断。

示例(`01-registry.mdc` 中的决策树):

```
Decision: 选择注册方式
→ IF 平台 = Forge → 使用该档 01-registry(DeferredRegister 或 RegistryEvent,禁止套用邻版)
→ ELSE IF 平台 = Fabric → 使用 Registry.register() in onInitialize
→ ELSE IF 平台 = Quilt → 优先 QSL / org.quiltmc(见 quilt/<ver>/01-registry.mdc),不要生成 FAPI Registry 当 QSL
→ ELSE → 询问用户
```

## 第五步:查阅知识库(遇到问题时)

1. 先查阅 `09-anti-patterns.mdc` 看是否是已知错误模式
2. 再查阅 **确认平台与版本后** 的 `平台/版本/knowledge/`(例:`forge/1.20.1/knowledge/`):
   - `antipatterns/` — 按症状分类的反模式(registry / item / block / entity / events / networking / gradle)
   - `version-changes/` — 版本迁移指南(1.19.x / 1.20.x 等)
   - `common/` — 术语表、数据包/资源包格式速查
3. 根目录 `knowledge/patterns/` 仅短片段模式库(非完整 antipatterns)
4. 实务问题(发布 / 崩溃分类 / 软依赖 / 机器 GUI)→ MCP `search_community_docs`(仓库根 `community_knowledge/`)
5. 如果仍无法解决,询问用户当前使用的具体版本和平台

### 使用社区自写短文时(强制)

完整规则见 `community_knowledge/AGENT_USAGE.md`。摘要:

- 社区短文 **不替代** 官方文档 / `query_api`
- 依据某篇 `authored/` / `permitted/` / `links/` 写方案时,若 **不清楚、不会、缺方法名、与现象对不上** → **必须先打开短文给出的原文 URL 或官方文档**(`WebFetch` / 浏览器 / `get_*_doc_full`),禁止臆造
- `links/`(如 6071)仅外链浏览,**禁止**把网页正文拷进回复当「已入库全文」

## 库模组 Skill(knowledge/libs 源稿即用)

涉及常用库模组(配置库 / 饰品 / GeckoLib / Patchouli / CCA / Polymer 等)的 Skill **不落盘**到平台 `.cursor/skills`,一律按解析规则直接读根目录源稿:

1. 平台 → 组映射:
   - `forge` → `forge-only` + `all-platforms`
   - `fabric` / `quilt` → `fabric-only` + `all-platforms`
   - `neoforge` → `neo-only` + `all-platforms`
   - `bedrock` → `bedrock-only`(Script API 等;禁止把 CCA/Trinkets/GeckoLib 当基岩教程)
   - LiteLoader / Rift / ModLoader:暂无独立 Java 库组;不要把 Fabric/Forge 库 Skill 当这些加载器的 API
2. 在组内按名称找 `knowledge/libs/<group>/mc-<name>/SKILL.md`,**直接读源稿**,不要查平台 `.cursor/skills` 的库项(那里已清理,不存在库项)
3. 用 frontmatter 二次过滤:`platforms`(组是主依据,白名单防组内误放)、`mcVersions`(留空/未写 = 不限版本;非空则必须包含目标 MC 版本;与 knowledge/libs/README.md §3.6 及解析代码一致)
   - **版本标记是坐标唯一真值(2026-09-16 起)**:库 skill 若带 `versions.json`(当前:`knowledge/libs/fabric-only/mc-cloth-config/versions.json`),**写依赖坐标前必须读它对应 MC 版本的 slot**(`coord` / `state` / `basis`);**`state != active`(`commented` / `todo`)一律不采纳**——`todo` 即 `TODO(未核实)`,**不许按邻居档推**。中心稿 SKILL.md 的「版本映射表」是证据说明,**不是取值入口**。
   - **档内手稿优先(档内工程)**:`fabric/<v>/.cursor/skills/mc-<lib>.md` 的 `<!-- cloth-version-inject v=… coord=… state=… textApi=… -->` 标记行是该档实况,由 `scripts/project-cloth-skill.mjs` 按 versions.json 回填(**禁止手改标记行**)——直接读它,不要从中心稿表格抄。
   - 这两条由 `mcp-server/test-core.mjs` §S14(注入标记 ↔ versions.json 一致性)与 §S15(`resolve-lib-skills --validate` 解析链校验)自动闸住;维护侧失步即红。
4. 不确定该用哪个库 Skill → 先读 `knowledge/libs/all-platforms/mc-lib-catalog/SKILL.md`;完整清单见 `knowledge/libs/README.md`
5. **禁止**把 Fabric 专属库(Trinkets / CCA / Polymer / Text Placeholder 等)当 Forge 教程;Forge/NeoForge 饰品用 `mc-curios`(`forge-only`),Fabric 用 `mc-trinkets`(`fabric-only`)
6. **配置不要新写树级 `mc-config` Skill**:配置**原则**一律读 `knowledge/libs/all-platforms/mc-config/SKILL.md`(工作流 `mc-config`)。**调用 `generate_config` 只限工具支持的四个平台 —— `forge` / `neoforge` / `fabric` / `quilt`**(`mcp-server/src/generators/index.ts` 的 `generateConfig(loader)` 枚举即此四者,且它没有 `platform` 参数);**`LiteLoader` / `Rift` / `ModLoader` / `基岩` 不要调用 `generate_config`**(平台面不含它们,只有拒绝文案)——这些档的配置走各档自述 / pack JSON / Script,也不要套 Cloth / ForgeConfigSpec。
   - **选型建议与工具默认不同源不是矛盾**:`generate_config` 在 fabric / quilt 上**默认永远吐 Cloth Config 骨架**;YACL 只能作为**用户显式 opt-in** 的结果出现(`library` 参数已实现:枚举 `cloth | yacl`,**默认 `cloth`**,不传即现状;只有显式 `--library=yacl` 才换 YACL 骨架,禁止把默认改成 yacl)。
   - **第三方配置库不是 loader API(强制)**:Cloth / YACL / Fzzy / owo 等的方法名与注解,必须先由用户自备 jar 走 `ingest_loader_api` 入库(默认 dryRun,只写 `$MC_SKILL_CACHE/loader-api-summaries` overlay,禁写仓库 `data/`)并逐签名核对后才能写进骨架。**`library=yacl` 尤其如此:该用户必须另外对自己的 yacl jar 跑一次 `ingest_loader_api`,否则返回的只是带 `// TODO(未核实)` 的结构壳、编译不过。**未入库 → `query_loader_api` 只回 `found:false`,此时**只能**留 `// TODO(未核实)`,禁止凭训练记忆补方法链。
   - 本条与上面第 5 条同源:平台库 Skill 按组映射,`generate_config` 按 loader 枚举。两个面都由 `mcp-server/scripts/assert-config-platform-face.mjs` 与本条正文对齐,**单侧改动即红**(子档改了禁令而根纲没跟上、或根纲写成「一律 generate_config」,都会被抓)。

## 不确定时

永远选择**保守**方案:
- 不确定用哪个事件 → 选更通用的事件
- 不确定方法名 → **先语义搜索**:`search_forge_docs` / `search_fabric_docs` / `search_neoforge_docs` / `search_docs`(语料与语义索引按档在盘,命中页面可直接引用);IDE 自动补全次之。`query_api` 之类是**兼容工具**(每次调用响应都带兼容警告,仅作兜底;仅 Vanilla/Parchment,约 1.16.5–1.20.4;**不含** Forge/Fabric 类。**1.12.2–1.13.2 可能 found:true 但 methods 为空;1.14.4/1.15.2 api-index 为空是设计边界——工具会报边界并推荐语义搜索;26.1+ 无索引**)。**api-index 未命中时响应可能带 `nameIndex`(在盘映射索引的第二档出处,2026-09-25):它只证「该档映射里有/没有这个名字」+ 成员数与名字样本,`found` 仍为 `false`,签名与用法不得由它推定**。平台 API 摘要用 `query_loader_api`(同为兼容工具)。Forge 1.12.2 教程用 `search_forge_docs`(`version=1.12.2`),不要用 `query_api` 核 `Block` 构造。
- 不确定是否跨平台 → 明确标注 `// Forge only` 或 `// Fabric only`
- `DOC_NOT_FOUND` / 无本档规则树:保持未核实骨架,**禁止**用邻版 API 补全。空 stub 不是漏写。

## MCP Server 工具(可选)

如果项目根目录下存在 `mcp-server/`(即本项目 `MC_skill`),可以使用本地 stdio MCP(服务名 **`MC-AI-Coding-Assistant-Tool`**;需 Node **>= 22.5**(**22.5–22.12 与 23.0–23.3 需 `--experimental-sqlite`**,22.13+ / 23.4+ 免),`MC_SKILL_DATA` 指向 `data/`,可选 `MC_SKILL_COMMUNITY`):

| 工具 | 功能 |
| --- | --- |
| `query_api` | **兼容工具(每次调用响应带兼容警告,优先用语义搜索)**:按类名查询 Vanilla/Parchment API 签名(约 1.16.5–1.20.4;1.12.2–1.13.2 类名空壳;**1.14.4/1.15.2 api-index 为空是设计边界——工具会报边界并推荐 `search_forge_docs` 语义搜索**;26.1+ 无索引)。精确 FQCN 或唯一简名才 `found:true`;`Handler` 等歧义子串 `found:false` + suggestions。**第二档出处(2026-09-25)**:api-index 未命中 / 该档压根没有索引(如 1.21.x Fabric)时,响应多一个 `nameIndex` —— 它查的是在盘 `data/<平台>_<版本>/mappings/yarn-mappings.sqlite`,**只证「这个名字在该档映射里存在」+ 成员数与名字样本,不证签名、不证用法**;`found` 恒仍为 `false`,`nameIndex.mappingEra` 必须读(`yarn-tiny` 的 named 是 Yarn 名,`forge-srg`/`tsrg`/`mcp-csv` 的 named 是 MCP `func_/field_` 名,`mcp-config-srg`(Forge 1.16.5–1.20.4 六档)的 named 是 SRG 类名 + `m_N_/f_N_` 成员名,1.16.5 那档为老形 `func_/field_`);**这一档出处按 Forge 线选库**(本工具的 api-index 只读 `data/forge_<ver>/extracted` ⇒ 1.16.5–1.20.4 这六个「两棵树都有库」的版本不再由 fabric 的 yarn-tiny 代答;forge 无库时照旧回落 fabric,如 1.21.1 仍给 Yarn 名);要签名仍走 `search_*_docs` 或 `get_minecraft_source` |
| `get_method_params` | 查询方法参数名(可选 version) |
| `convert_mapping` | 在 **mojang / mcp / yarn / parchment / obfuscated / intermediary** 间互转类 / 方法 / 字段(Yarn 走预建 SQLite 惰性点查;1.12.2 用 MCP SRG;`to=mojang` 与 `obfuscated` 同为 Tiny official 短名;yarn-tiny 档 fabric **1.14.4–1.21.x** 无 MCP/Parchment 层,`to=mcp`/`to=parchment` → `YARN_TINY_NO_MCP_LAYER`;**26.1+ 已去混淆 → `UNOBFUSCATED_NO_YARN`,禁止转 yarn**)。**S2/S3(2026-09-25)**:`memberName` 可用逗号/分号/换行传 ≤50 个名字做批量转换(顶层仍是第一个名,另给 `results[]` + `batch{requested,found,missing}`;超上限报 `INVALID_INPUT`,不静默截断);`accessLines=true` 附可直接粘贴的 AT / AW 条目行,**名字层按加载器分叉且不可混用**——Forge AT 成员行必须 SRG 名(`data/forge_1.20.1/…/advanced_accesstransformers.md:55`),NeoForge AT 成员行用可读名 + 粘连描述符(`data/neoforge_1.21.1/` 同页 `:127,:137`),Fabric/Quilt AW 条目的名与描述符须与工程当前映射层一致(`data/fabric_1.21.11/…/develop_class-tweakers_access-widening.md:82`),头由 `to` 层决定(named / intermediary / official)。取不到 SRG 名或缺描述符 ⇒ 行内留 `<TODO…>` 且 `complete:false`,**禁止拿可读名顶替 SRG 名**;`complete:true` 的行会回灌 `validate_at`/`validate_aw` 用的同一解析器(`selfCheckOk`)。**SRG 成员层已扩(2026-09-26)**:Forge **1.16.5 / 1.17.1 / 1.18.2 / 1.19.4 / 1.20.1 / 1.20.4** 六档现各有从 MCPConfig 削减件建出的库(`mappingEra=mcp-config-srg`,`named` 列 = SRG 名 —— 1.17+ 是哈希形 `m_/f_`,1.16.5 是老形 `func_/field_`;`official` 列 = mojmap 可读名)⇒ 这六档的 Forge AT 成员行出实名;该库**只在 `platform` 显式为 `forge` 时参与选库**,无平台或 `fabric` 查询照旧走 `YARN_TINY_NO_MCP_LAYER` 拒答。全部 `neoforge_*` / `quilt_*` 仍无成员库 ⇒ 那些档照旧 `<TODO…>`。**`from=mojang` 有两档出处(2026-09-27 落地)**:第一档 = 该档映射库的 `official` 列,而那一列对**被混淆的类**存 **Tiny 混淆短名**(a/fac/ccv),只有本来不混淆的少数类带可读全路径(13 档 100 985 行实测:短名 100 310 /可读路径 675 = 0.67%,如 `com/mojang/blaze3d/…`)⇒ 拿 mojmap 可读名点查在 13 档 fabric/quilt 库里必然未命中,未命中的回执现在自带这条层披露,**禁止**把 `found:false` 读成「该版本没有这个类」;第二档 = 派生对照表 `data/_yarn-mojmap-pairs/`(类名级 obf/mojmap/yarn + 两侧 FQCN,**无 intermediary 列**),只在 `from=mojang` 且 `to` 为 `yarn`/`obfuscated` 时参与,命中带 `fallbackUsed:true` 且 notes 点名是哪张表、该表多少条;`to=intermediary` 明确拒答而不是回落第一档报「未找到」 |
| `get_server_status` | 预热/数据路径与 descriptor 自检(含 updateHint);另返回 **`java`** 探测(`node` / `JAVA_HOME` / `version` / `ready` / `hint`,反编译与 remap 需 JDK 17+)。**只报本机 Java 现状,不做 Gradle ↔ JDK 匹配判定**(那走 `diagnose_gradle`) |
| `get_version_info` | 查询版本支持的 API 范围(**仅 Forge**)。schema 的 `required` 只有 `version` + `action`;`platform` **不在 required 里,但实现上必须显式给 `forge`**——缺省或非 forge 返回 `WRONG_TOOL`(`mcp-server/src/version/index.ts:280`) |
| `mc_skill_update` | 检查/应用 tooling+data 更新(GitHub Release;确认后可写盘) |
| `diagnose_gradle` | 诊断 Gradle 构建问题。ForgeGradle + Loom + NeoGradle/MDG;liteloader 插件走轻量模式。Rift / BaseMod / 基岩仍早退。 |
| `generate_datagen` | 生成数据生成器代码 |
| `crash_analyze` | 分析崩溃日志 |
| `validate_project` | 校验模组项目结构。Forge / Fabric / Quilt / NeoForge 真检查;LiteLoader/Rift/ModLoader/基岩 skipped。坏 recipe 只 warning。 |
| `check_publish_ready` | 发布前清单(license/version/`build/libs` + `community_knowledge` publishing.md 清单,缺项只 warning)。不上传、不调外网发布 API。 |
| `inspect_runtime` | 日志型 inspector。优先 `logsDir`;否则有界探测 `run/logs`。禁止全盘 / JVM attach。 |
| `generate_playtest_driver` | 游玩自测骨架(只吐文本):默认 `external_bridge`(桥动作序列 + 后置条件 + 证据约定);`in_jvm_player_agent` 仅结构壳。 |
| `inspect_playtest_evidence` | 读游玩自测证据(exit-code / state.json / `[QA]` / calls.jsonl / 截图),三态 `present\|absent\|unreadable`;须 `MC_SKILL_PLAYTEST_ALLOW=1` + `MC_SKILL_PLAYTEST_ROOT`。 |
| `playtest_bridge` | 调 BlackBoxPro 桥(`127.0.0.1:38081`):`status` / `execute` / `await`;超时映射 `PLAYTEST_TIMEOUT`;`execute`/`await` 须 `confirmed=true` + 授权;桥无鉴权,只在本机用。 |
| `detect_mod_project` / `activate_platform_pack` | 探测工程;`session` 加载规则/Skill 索引(默认 00/01/09),`write` 写入用户工程(见根 README「规则包加载」) |
| `query_loader_api` / `search_loader_api` / `ingest_loader_api` | **`query_loader_api` 是兼容工具(每次调用响应带兼容警告,优先用语义搜索)**:加载器/模组 API 逐签名摘要(必填 platform+minecraftVersion;覆盖以已 ingest 的档为界)。**不是** `query_api`。ingest 把用户自备 jar 抽成摘要,只写 `$MC_SKILL_CACHE/loader-api-summaries` overlay,禁止写仓库 `data/`。**同一平台多套构件时 `library` 选键位(2026-09-28)**:`platform=fabric` 的候选键是 `<ver>-fabric-api`(API 库)与 `<ver>-fabric`(**加载器本体**),写侧从前固定取第一个候选键 ⇒ 拿 fabric-loader jar 不传 `library=fabric` 会**覆盖掉 API 摘要**;现在传 `library` 按后缀选,后缀不在候选里 ⇒ `INVALID_INPUT` 并列出候选(不新造键名、不猜)。仓库 `data/loader-api-summaries/` 现有 14 份 `<ver>-fabric.json`(fabric 全档),`query_loader_api platform=fabric` 在该档没有 API 摘要时会读到它(`candidateKeys` 顺序:API 优先) |
| `search_forge_docs` / `get_forge_doc_*` / `list_forge_versions` | Forge 文档。先 `list_forge_versions`;**1.12.2 用这套**,不要用 `query_api`。与 `search_docs({platform:"forge"})` 等价 (`list_*_versions` 列的是**本仓库已入库**档位,不在清单 ≠ 上游没有文档) |
| `search_fabric_docs` / `get_fabric_doc_*` / `list_fabric_versions` | Fabric 文档。先 `list_fabric_versions`;查询参数用入库档名(如 `26.1.2`),不要把工程 `minecraftVersion=26.1` 当参数名 (`list_*_versions` 列的是**本仓库已入库**档位,不在清单 ≠ 上游没有文档) |
| `search_neoforge_docs` / `get_neoforge_doc_*` / `list_neoforge_versions` | NeoForge 文档(1.20.1 回退 Forge)。先 `list_neoforge_versions` (`list_*_versions` 列的是**本仓库已入库**档位,不在清单 ≠ 上游没有文档) |
| `search_docs` / `get_doc_*` / `list_doc_versions` | 跨平台通用文档入口(`platform` 含 forge/fabric/neoforge/**quilt**/liteloader/rift/modloader)。Quilt 问 QSL 时禁止把 Fabric Registry 当命中。`list_doc_versions` 列的是**本仓库已入库**版本;不在清单 = 本档无语料树,不等于上游没有文档 |
| `search_bedrock_docs` / `get_bedrock_doc_*` | 基岩版 Microsoft Learn;带滞后 `docsStatus`。不是 `search_forge_docs` |
| `analyze_bedrock_log` | 基岩 content-log 分诊(`content_log.txt`)。**不是** `crash_analyze` |
| `validate_addon_manifest` / `validate_bp_json` | 基岩 pack 校验;不是 `validate_project` / `validate_datapack_json` |
| `generate_addon_manifest` / `generate_bp_entity` | 只吐 JSON 文本,不写盘 |
| `list_community_sources` / `search_community_docs` / `get_community_doc_*` | 社区实务知识库(发布/崩溃/软依赖;不替代官方文档) |
| `analyze_porting_path` / `port_project` | 移植分析与脚手架动作 |
| `query_upstream_releases` | 查**上游发布源**:某个加载器/映射/模组的版本到底存不存在、最新出到第几 build(`forge` / `neoforge` 走 maven-metadata,`fabric-loader` / `fabric-yarn` / `quilt-loader` / `parchment` 的端点按 MC 版本分列、**必带 `minecraftVersion`**,`modrinth` 走 `slug`)。**需联网**;TLS 失败自动回退 curl,不改系统证书库。**S4′ 磁盘缓存(2026-09-25)**:默认吃缓存并按档分 TTL —— `available:true` 存 6 小时、`available:false`(上游确实没有)存 **2 小时**(证否会被新版本推翻,禁止按长档冻结)、`ok:false`(没查到)**不缓存**;缓存根只许 `$MC_SKILL_CACHE/upstream-cache/`,解析到仓库内一律拒写。每次响应带 `cache{hit,tier,ttlMs,ageMs?,wrote?,note?}` 说明这一发到底从哪来;`refresh=true` 强制打上游,`MC_SKILL_UPSTREAM_CACHE=0` 整体关掉。键覆盖 `source`+`minecraftVersion`+`slug`+`limit`(`limit` 不在键里的话旧缓存会把新请求悄悄截短) |
| `diagnose_data_paths` | 诊断数据目录与 `community_knowledge` 配置 |
| `query_registry` / `mixin_analyze` / `audit_resources` / `validate_datapack_json` | Registry ID、Mixin、资源与数据包校验 |
| `get_workflow_template` / `list_knowledge_resources` / `read_knowledge_resource` | 工作流全文(仅完整流程才调,改已有代码不要调;**人在环清单,不是无人值守流水线**;不代跑用户 Gradle / 不拷 jar / 不上传)与知识 URI |
| `generate_model` / `generate_lang` / `generate_network_packet` / `generate_capability` / `generate_config` / `generate_entity_renderer` / `generate_worldgen` | 代码/JSON 骨架生成(见根 `README.md`)。**默认只吐文本 + `suggestedPath`**;真写需 `write=true` + `confirmed=true` + `MC_SKILL_ALLOW_WRITE=1` + 绝对 `MC_SKILL_PROJECT_ROOT`,且路径相对工程根不含 `..`。**三态语义(2026-09-19)**:默认不传 `write` = dry-run(恒 `ok:true` + `resultKind:"ok"`);**生成失败**(版本/平台不支持等)⇒ `ok:false` + `resultKind:"generation_failed"`(原因在 `errors[]`);**写入未完成**(缺 `confirmed` 或写盘被沙箱拒)⇒ `ok:false` + `resultKind:"write_blocked"`(CLI `success:false` + exit 1,写盘未发生,文本预览仍在 `result`,细因在 `writeError.code`)——两种失败不得混读。**参数名不统一,禁止一律写 `platform`**(实测 `node mcp-server/dist/cli.js list-tools`,2026-09-13):`generate_config` 只有 `loader`(**无 `platform` 参数**)+ `version`;`generate_model` / `generate_lang` **既无 `platform` 也无 `loader`**,只必填 `version`;`generate_network_packet` 必填 `platform` 且**没有 `version` 参数**(版本写在带后缀的 platform 里,如 `forge_1.20.1`);只有 `generate_capability` / `generate_entity_renderer` / `generate_worldgen` 同时必填 `platform` + `version`。全部禁止默认 forge。**`generate_lang` 的空 `entries` 不静默(2026-09-25 S6-③ 落地,2026-09-26 补上文档面)**:传 `{}` 或空串仍 `ok:true` + `resultKind:"ok"`,但响应里多一条披露 warning + 机读字段 **`emptyEntries:true`** ⇒ 看到该字段就知道吐的是空骨架(只带头注释、没有 lang 键值行),别当成品,也不要靠读那句中文 warning 分支。 |
| `localize_mod` | 模组汉化:diff/draft_zh / jar extract/pack_draft(无机器翻译) |
| `analyze_log` / `analyze_build_log` / `get_migration_guide` / `check_dependencies` | 游戏日志、Gradle/javac 构建日志、迁移与依赖提示 |
| `lookup_obfuscated` | 崩溃短名反查 |
| `get_minecraft_source` / `decompile_mod_jar` / `search_mod_code` / `analyze_mod_jar` / `download_official_mdk` | 按需反编译与 jar 元数据;`download_official_mdk` 拉官方 MDK 到 `$MC_SKILL_CACHE`(**默认 dryRun**,校验和钉在 `mcp-server/data/mdk-checksums.json`);必填参数只有 `platform` + `minecraftVersion`,其余(`buildPlugin` / `destPath` / `allowUnpinned` 等)可选。`search_mod_code` 源码未生成时 `NOT_DECOMPILED`(不是 `NOT_FOUND`),先调反编译。 |
| `validate_at` / `validate_aw` | AT / AW 字节码校验 |
| `resolve_lib_skills` | 库 skill 解析(平台 + 精确 MC 版本;与 CLI `lib resolve` 同一 core;只解析不返回正文 —— AI 仍直接读 `knowledge/libs` 源稿;带 `versionsJson` 的库写坐标前先读该文件 slot) |

### 工具边界(禁止误判)

完整对照表见根目录 `README.md`「工具边界」。调用前必须遵守:

- **优先语义搜索;`query_*` 是兼容工具**:查 API/文档/机制,先用 `search_forge_docs` / `search_fabric_docs` / `search_neoforge_docs` / `search_docs`(语义索引按档在盘),`query_api` / `query_loader_api` 仅作兜底——它们的每次响应都带兼容警告,1.14.4/1.15.2 还会显式报告「api-index 为空」边界。
- **检索命中 ≠ 该名字存在(verbatim 逐字支撑位,2026-09-20)**:上面那条**先语义搜索**的顺序不变;`search_*_docs` / `search_docs`(含 quilt 两腿)在查询是**单个标识符形态**(类名 / 方法名 / FQCN / 资源路径;散文与 `|` 分组不判)时,每条命中多带一个 `verbatim`——`true` = 该名字在该页正文**逐字**出现(**点号 / FQCN 查询降级成「末段类名逐字」这一层**:语料几乎不写点号全名,实测 dotted 查询的 `true` 基本都是末段命中,所以命中层写在行级 `verbatimOn`、整体构成在 `verbatim_summary.matchedOn` = `term`/`tail`/`mixed`;读到 `tail` 就意味着**不能**据此断言你输入的那串点号全名在语料里出现过);`false` = 已读到该页正文且确认没有;**没有该字段 = 未判定**(薄档 / primer / porting 旁路 / 取不到正文),未判定**不等于**「语料里没有这个名字」。顶层另有 `verbatim_summary:{term,judged,hits,matchedOn}`,`hits:0` 时结果仍可能主题相关,但不构成该名字存在的证据:要签名就 `get_*_doc_full` 读正文,确认不了就留 `// TODO(未核实)`。该位只做事后标注,不改命中集合、顺序与 `total`(闸:`mcp-server/scripts/assert-verbatim-support.mjs`)。
- **`total` 只是本次条数,判截断看 `totalPool` / `truncated`(2026-09-28 落地)**:8 个文档检索发射点(`search_forge_docs`/`search_fabric_docs`/`search_neoforge_docs`/`search_docs` 的 forge·quilt 自有语料腿·quilt 回退 Fabric 腿/`search_community_docs`/`search_bedrock_docs`)的载荷**无条件**带两位(不传 `limit` 也在):`totalPool` = 进输出窗口之前该面手里可用的候选条数,`truncated` = (`total < totalPool`)。⇒ 看到 `n === total` 不再需要猜「池就这么大」还是「窗就这么大」:`truncated:false` = 本面在手候选已取尽,`true` = 还有页没返回(放宽 `limit` 或换 `tags` 可取回)。**三条边界**:① 该池只到「本面可得」这一层,**不是语料全量**(fusion 输入与 L0 检索各有上限;无语义库时池就等于 L0 命中数);② `limitWindow.candidates` 在多数面仍是**窗口**不是池,别拿它当分母;bedrock 面的窗口块挂在 `demotion.{requestedLimit,resultLimit,candidateLimit,candidates,limitMax}` 下而不是顶层 `limitWindow`,**这是契约不是待修项**(`L115` 裁定关闭):该面截断态在顶层无条件有两位,且 `demotion` 五键默认就发 ⇒ 默认路径上它的窗口披露比另 6 面更多(那 6 面的 `limitWindow` 是「传了 limit 才加」);残余是**按 `limitWindow` 统一读的消费者仍须为 bedrock 特判键名**——关掉的是「截断态不可得」这个缺陷,不是形状差异;③ 这一族与 `search_loader_api`/`query_upstream_releases`/`search_mod_code` 的 `total`(那三面报的才是全池)**符号相反**,照抄会反。执法:`mcp-server/scripts/assert-total-semantics-wording.mjs` 判据 6(真跑 **11 面 × 两档** = 发射点桶 8 个具名 label + 平台路由桶 3 面;高档的 limit 从 `dist` 现取该面 `limitMax`,门里不抄数字。腿含:**同一面跨档 `totalPool` 必须相等**(专抓「池从窗口里取」)、反向腿「高档至少一面取尽」防恒真、低档至少一面截断防恒假、发射点桶按名点名 + 面数地板)。
- **`found:false` ≠ 游戏里没有该类**:多半是索引覆盖范围外,或简名歧义(`Handler` 不会命中 `MouseHandler`)。1.12.2 **空壳**(`found:true` + 空 methods)与 26.1+ 零类不同;**1.14.4/1.15.2 是空索引档(该档 docs 语料与语义索引反而完整)**;Forge 特有类改 `query_loader_api` / `search_*_docs` 或反编译。
- **加载器/模组 API 的静态字段名不在 `query_loader_api` 能力内**:摘要只收类 + 方法签名,`fields` 恒为 0(实测 `mcp-server/data/loader-api-summaries/1.18.2-forge.json`:classCount 988 / methods 4950 / **fields 0**,且 `ForgeRegistries` 在 `classes` 与 `fqcnIndex` 里都没有条目)⇒ 它**答不了字段名**,也不要用 `ingest_loader_api` 去「复现」字段名结论。字段名按**四档出处**落笔,四档不得混称:**① 语料逐字**(本仓 `data/**` 上游正文可复核——**仍然只有** `BLOCKS` / `ITEMS`,如 `data/forge_1.18.2/forge-docs/1.18.2/processed/concepts_registries.md:24,96`、1.19.4 同名页 `:24,106`);**①b 官方构件逐字**(2026-09-24 新增档,自备 jar 可复核、构件不入库):1.18.2 的 `ENTITIES` / `BLOCK_ENTITIES` / `SOUND_EVENTS` / `PARTICLE_TYPES` / `CONTAINERS` / `PAINTING_TYPES` / `FLUIDS` 由官方 1.18.2-40.1.80 源码(157 行)+ 1.18.2-40.3.12 universal jar `javap -p`(41 行 / 注册表字段 32 个)两 build 两机制互证 ⇒ 旧记「② 处方-only」的 `SOUND_EVENTS` / `PARTICLE_TYPES` / `FLUIDS` 与「③ 外部-only」的 `ENTITIES`@1.18.2 **已升到本档**;**② 处方-only**(只有本档 rules/skills 自撰、无外部出处:forge/1.18.2 面**现已清空**);**③ 外部-only**(仓内不可复核:`ENTITY_TYPES`@1.19.4 靠官方源码 + Forge 40.2.1 javadoc + 1.18.2→1.19 改名时点,本仓无落盘副本;**1.19.4 侧字段名与 1.20.x 的 `MENU_TYPES` 本次未取证,禁止拿 ①b 外推**);**④ 证伪(不是未核实)**:`FLUIDTYPES` 与 `FLUID_TYPES`@1.18.2 —— **1.18.2 没有流体类型注册表**,流体走 `FLUIDS`(`FluidType` + `FLUID_TYPES` 自 1.19 才有),修法是删/改写那些表行、**不是**改名成 `FLUID_TYPES`;复核入口需自备 jar + JDK 17+,两条各一行:`unzip -p <forge-1.18.2-40.1.80-sources.jar> net/minecraftforge/registries/ForgeRegistries.java`、`javap -p -classpath <forge-1.18.2-40.3.12-universal.jar> net.minecraftforge.registries.ForgeRegistries`。⚠️ **禁止把本档规则行当外部出处(那是循环引证)**——`forge/1.18.2/.cursor/rules/01-registry.mdc:27`、`forge/1.19.4/.cursor/rules/01-registry.mdc:27` 是**处方实况**(而且正是裁定自己改过的行),只能当「现状长什么样」读。四档都给不出的名字留 `// TODO(未核实)`,禁止凭训练记忆补。
- **`search_*_docs` 查 `constructor` 崩溃**:旧 bug(`Object.prototype`);已修。改完 `mcp-server` 后必须 `npm run build` **并重载 MCP**,或用 `node mcp-server/dist/cli.js` 验证。
- **平台工具不要混用**:`get_version_info` 仍仅 Forge。`diagnose_gradle` 覆盖 ForgeGradle + Loom + Neo/MDG;liteloader 插件走轻量模式;Rift / BaseMod / 基岩仍早退。`validate_project` 对 Fabric/Quilt/NeoForge 做真检查,LiteLoader/Rift/ModLoader/基岩 skipped。基岩用 `validate_addon_manifest`。
- **文档 fallback 仅限查询 API**,不代表规则树可用;命中邻近版时结果含 `fallback: true` 与 `source_version`。本版无树则 `PACK_NOT_FOUND`。
- **「本仓没入库」≠「上游没有」**(`query_upstream_releases` 存在的理由):`list_forge_versions` / `list_fabric_versions` / `list_neoforge_versions` / `list_doc_versions` 列的都是**本仓库已入库**的文档档位,不在清单只说明本仓没抓过。要回答「上游有没有该版本 / 最新到哪个 build」用 `query_upstream_releases`,并按三态读:`ok:false` ⇒ 没查到(网络/HTTP/解析),**禁止**据此断言上游没有;`ok:true` + `available:false` ⇒ 上游确实没有。`matchRule` 回显本次的版本归属规则(neoforge 尤其要记:MC `1.21.1` → 版本前缀 `21.1.`,不带前导 `1.`)。
- **文档 `id` 只用搜索结果**,不要用网站 URL;全文一次 ≤ 2 页。
- **社区短文不能当 API 规范**(`community_knowledge/AGENT_USAGE.md`)。
- **CLI 的 schema 拒绝已机读(2026-09-25 S6-②)**:`errorKind:"validation"` 的信封另带 `fieldErrors[]`(`{field, code, message, expected?, received?, enumValues?}`;缺必填折成 `MISSING_REQUIRED`,枚举违规给 `INVALID_ENUM_VALUE` + `enumValues`)。要按字段分支就读这个键,**不要再解析 `error` 里那条中文串**(串仍在,给人和旧消费者读)。`mcp-server` 内部生成器那层的机读码是 `result.action.code` + `resultKind`(见 A1),两套不是一回事。
- **人在环 / 写盘类默认 dryRun**(`port_project` / `mc_skill_update apply` / `activate_platform_pack write`);`generate_*` 只吐文本。`get_workflow_template` 是清单不是流水线。Gradle、拷 jar、上传发布须用户确认后执行,不要当成漏实现的自动编排。
- **不要克隆版本文档**(1.21 wiki ≠ 26.1.2;26.1 ≠ 26.2)。
- **正文里的 `<<< @/…` 与 `@[code …]` 是转引标记,不是可照抄的代码**:`get_doc_full` / `get_fabric_doc_full` 返回的正文已由 reader(`docs-platform/fabric/transclude.ts`)展开成围栏代码块,块尾带 `<!-- source: … -->`(实测 26.1.2 `develop_networking`:21 处展开、0 处裸标记)。若返回正文里**仍有裸标记行** ⇒ 该页取件目标未落盘,属缺陷:不要把标记贴给用户,也不要凭训练记忆补正文,改口 `query_loader_api` / 用户自备 jar 核实。
- **「本档 docs 零命中」≠「该 API 不存在」**:官方文档的示例常写在 `<<< @/reference/...` include 内,`reference/` 是另一棵目录;检索按页面正文计。同理 `query_api` 的 `found:false` 只说明索引未覆盖。
- **口径以 [`CONTRIBUTING.md`](./CONTRIBUTING.md) §数据链口径为准**:标签读法(`[Label]` 是标签页标题、非数字花括号是选项、区段名先逐字再 `-`↔`_`)、计数器分母(`sites`/`expanded` 只算 `@[code`,`<<<` 走 `angleSites`)、台账与豁免规则、`packages` 归属与「不可当 import 依据」。
- **验证纪律见 [`CONTRIBUTING.md`](./CONTRIBUTING.md) §验证纪律**:改了 `mcp-server/scripts/**` 或 `scripts/**` 后,收口**必须**跑第 8 步(`cd mcp-server && node test-scripts.mjs`)——`npm test` 里抽跑几道门**不能**代替它;harness 里的硬钉锚点/计数只许「先对齐生产侧、再改 harness」,禁止靠删断言变绿;`npm test` 不得与语料抓取并发(4000 ms lag 门与磁盘负载耦合,会假红)。CLI 侧另有两档独立门:`npm run test:cli:quick`(`mcp-server/scripts/assert-cli-quick.mjs`,进默认门链)与 `npm run test:cli:full`(`mcp-server/scripts/assert-cli-full.mjs`,全量档(权威名单跑时现取),不默认跑;改动 CLI 入口/退出码/信封后应补跑)。

### 工具不可用排查(clone 后必读)

- **MCP 工具全部调用失败(服务未启动)**:说明 `mcp-server/dist/` 未编译(dist 不入库)。执行:
  ```bash
  cd mcp-server && npm ci && npm run build
  ```
  (Node 需 >= 22.5;Yarn 映射可再 `npm run build:yarn-sqlite`。配置宿主见 `AUTO_SETUP.md`:先识别 IDE/CLI,再按该宿主的文件与顶层键合并草稿,不要默认写 Cursor 的 `mcp.json`。)
- **无 MCP 客户端时**:可用独立 CLI 调用任意工具——`node mcp-server/dist/cli.js <工具名> --参数=值`(通用 dispatch,85 工具全可用;如 `search_docs` / `check_dependencies` / `analyze_mod_jar` / `resolve_lib_skills`)。工程类工具可加 `--project <dir>`(映射到 `projectPath`)。工具输出始终为 JSON;`--json` 不改变工具输出,仅为兼容保留;它只在交互式终端下影响 `--help` 的呈现(人读摘要 → 机器可读 schema),表达格式意图用 `--output-format json`(当前唯一合法值)。
- **CLI 双入口(2026-09-17 提级)**:**工具线** = 上面的 `dist/cli.js`(与 MCP 同一份 `toolHandlers`);**仓库线** = `node mcp-server/bin/mc-skill-scripts.mjs <lib|corpus|cloth|gate> <命令>`(薄壳转发 `scripts/` 与 `mcp-server/scripts/` 的既有脚本)。仓库线属**维护侧**作业(批量反编译、摘要重建、G1 门、注入标记回填),MCP 工具面不暴露;两者互不分叉;冒烟门 `assert-cli-smoke`(test-core §S16)。安装/链接 mcp-server 包后,两入口的 bin 名分别为 mc-skill 与 mc-skill-scripts。
- **`get_server_status` 返回 `buildStatus.buildRequired=true`**:src 有比 dist 更新的修改,需重新 `npm run build`,然后**重载宿主 MCP**(只编 dist 不够, AI IDE 进程仍跑旧代码)。
- **反编译工具报 `TOOLCHAIN_MISSING`**:需要 Java 17+(VineFlower/tiny-remapper);安装 Temurin 17+ 后重启 MCP,或按返回指引操作。
- **`search_mod_code` 报 `NOT_DECOMPILED`**:反编译源码尚未生成(按设计不入库),按返回指引先调 `decompile_mod_jar` / `get_minecraft_source` 按需生成。(此前本行误写 `NOT_FOUND`;`src/decompile/index.ts` 发的是 `NOT_DECOMPILED`。)
- **`PLATFORM_DATA_MISSING`**:对应平台文档数据缺失,先调 `diagnose_data_paths` 确认 `MC_SKILL_DATA` 指向本仓库 `data/`。

## 交付汇报(每轮交付强制)

**先判档:看本轮改动落在哪张面**(目录面可核对,不靠 agent 自称「这是哪种会话」)

- **不触发**:纯问答 / 概念解释 / 只读检索(不改盘、不产出代码或文件)⇒ 直接回答,不套本节任何档。
- **主档 · 模组开发交付(默认)**:交付物是给用户的 —— 用户自己的模组工程(本仓库之外的代码 / 配置 / 资源),或本轮只吐文本、只给用法 ⇒ 走「主档四块」。**这是常态,别往维护档套。**
- **维护档 · 仓库维护交付**:改动命中本仓库受管面 —— 8 个平台树(`forge/**`、`fabric/**`、`neoforge/**`、`quilt/**`、`liteloader/**`、`rift/**`、`modloader/**`、`bedrock/**`,含平台根文件、各版本档与 `scaffold/**`)、`data/**`、`knowledge/**`、`community_knowledge/**`、`mcp-server/**`、`scripts/**`、`agent-tools/**`、`openspec/**`、`ralphy-spec/**`,或根 `*.md` ⇒ 走「维护档六块」。**只要改的是「给 AI 读的结论」(API 名 / 常量 / 映射 / 版本口径 / 骨架 / 门 / 台账),哪怕只有一行也走维护档。**(这份清单是**手列快照**:新增顶层受管目录时必须同步补此处,维护提醒见 [`CONTRIBUTING.md`](./CONTRIBUTING.md) §贡献注意)
- **混合**:按面分开写 —— 用户工程部分用主档,仓库面用维护档。
- **只改了 `temp/**`(scratch)**:走主档,但第 3 块必须注明「产物只在 `temp/`,未落生效路径」。
- **兜底**:拿不准是否属受管面 ⇒ 按维护档;只要本轮动了仓库受管面,维护档六块就必须写,不得用主档少写。

### 主档 · 模组开发交付(默认。用户要的是:能不能跑、怎么验、有没有坑)

1. **改了哪些文件 / 给了什么**:路径 + 每处一句「为什么」;只输出未写盘的明说「未写盘」。写盘前先给清单 / `dryRun` 预览并经确认(「人在环」)。
2. **怎么验**:用户在自己工程里怎么跑、看到什么算过 —— 构建命令、`runClient` / `runServer`、进游戏后的操作(`/give`、GameTest、基岩 content log)、该看哪条日志、失败长什么样。用用户能照着做的说法写。
3. **未核实项与影响面**:留了 `// TODO(未核实)` 的位置 + 原因(缺哪份文档 / 哪个 jar);本轮动过的影响面 —— `build.gradle`、平台元数据(`mods.toml` / `fabric.mod.json` / `manifest.json` …)、mappings、新增依赖、loader 或 MC 版本,以及用户无需跟着改什么。
4. **需要你拍板 + 风险与回退**:待你定的选择(创意设计 / 版本取舍 / API 选型);改前是什么、怎么退回。

四块都写;某块确实没有就写一行「无」——用户要看到「你查过了」。

### 维护档 · 仓库维护交付(仅当改动落在知识库 / 工具面)

1. **改了哪些文件**:`路径:行/字段` + 每处一句「为什么」。新建/删除的门、脚本、语料目录单列。
2. **判据与反证**(条件块):动了门或断言才写,须给「能红」的证据(投毒输出、红的条目、对照组是否仍绿)。没动门就整块省略,不得用「已加门」占位。
3. **验证台账**:跑过的命令 + 退出码 + 关键数字;数字须带**分母 + 口径 + as-of**,禁止裸数字。没跑命令写一句「本轮未跑命令」。
4. **数据侧变更**(条件块):动了 `data/**` 或台账数值才写 —— 页数 / chunks / embedded / 台账的前→后,以及是否由该门 `*_RELEDGER=1` 重算(未重算须写「未重算 + 原因」)。
5. **未做清单**:逐条「没做」+ 原因(等拍板 / 越界需授权 / 已裁定不做);**已授权但未执行**的必须落在这里,不得写成「等你再说一遍」。
6. **自我更正**:本轮推翻了上一轮哪句话、改在何处;没有写「无」。

**省略 vs「无」**:条件块(2 / 4)不成立就整块省略;常驻块(1 / 3 / 5 / 6)检查过为空就写一行「无」。两档都不写 `n/a` 占位行 —— 占位行本身就是要避免的噪音。

**两档都受的硬约束**:

- 数字只能来自**本轮实测输出**;引用记忆里的旧数、「应该差不多」、按上游推断代替实扫 ⇒ 视为未验证并明写 `未核实`。
- API / 签名 / 常量 / 映射的名字,要么有本档出处(`search_*_docs` / `query_loader_api` / 用户自备 jar),要么留 `// TODO(未核实)`;禁止凭训练记忆补。
- 汇报里每个路径都要能被人打开核对;不能核对的注明 `temp/` 或缓存产物。

**维护档另加**:两机制不一致时以表与文件实况为准,不以工具自述为准;产物须落生效路径(只在 `temp/`、`$MC_SKILL_CACHE`、overlay 里算未修);删除类只做清单;提交由用户执行,agent 不 `git add/commit/push`。强度真源:本机 `.codebuddy/rules/*.mdc`(不入库)与 [`CONTRIBUTING.md`](./CONTRIBUTING.md) §验证纪律 重叠时**按更严者执行**。