feat: protect mod payloads with main-thread ownership in 1.1.17

This commit is contained in:
Disy committed 2026-10-06 17:36:00 +08:00
1 parent 691e949691
commit 763c015103
29 files changed
+1552 -53

No files matched your search

@@ -0,0 +1,76 @@
# 1.1.17 公共收包归属:候选验证记录
日期:2026-10-06。结论:公共入口与已列交互路径已实施;不是完整整合包兼容认证,也不是 ServerWarashi 修复。
## 环境与范围
Java 21 Zulu、Minecraft 1.21.1、NeoForge 21.1.244。仅使用仓库 `runs/server` 的 `interaction-regression` 世界、127.0.0.1:25579。没有修改用户正式世界、启动器 mods 或远端仓库。用户报告过的 NeoForge 21.1.233 未在本轮单独运行。
可选模组为用户本机实际 jar:Create 6.0.10.starlight、Create Aeronautics bundled 1.3.0.starlight(含 Simulated)、Sable 2.0.5.starlight、Veil 4.2.0.starlight、Jade 15.10.5.starlight;CC:Tweaked 1.118.0.starlight 单独运行。
## 证据层级
| 层级 | 已验证内容 | 证据 |
|---|---|---|
| 纯 Java | FIFO、至多一次、256/4096 容量、取消回收、64 项/2 ms 预算、关闭、重入完成、8 生产者 | `runPayloadWorkQueueRegression` 的 `PAYLOAD QUEUE PASS` |
| 实际 Minecraft/NeoForge 上下文 | Runnable/Supplier、返回值、主线程异常身份、异步 guard、取消、换世界失效、配置阶段原路径、嵌套、Sable 同步重入拒绝 | `PAYLOAD PHASE/ENTRY/ASYNC PASS` |
| 实际 Minecraft 生命周期 | 新循环、菜单暂停、退出中循环仍等待、停止不恢复、停止后晚到任务不能复建诊断 | `PAYLOAD LIFECYCLE PASS`、`PAYLOAD REVIEW PASS` |
| 实际可选模组和原生物理 | 真实子世界、真实把手 BE、真实包和 Veil 上下文、Rapier 约束的创建及松手销毁 | `SIMULATED NATIVE RED` 后 `SIMULATED HANDLE PACKET PASS` |
| 实际 Create / Jade | 调速写入、查询 BE、原始距离拒绝 | `CREATE CONFIG PASS`、`JADE REQUEST PASS` |
| 实际 CC | 显示器同一性、回调等待、80 个延迟 token、双维度 ticket/unload、恢复后继续 tick | 四个 `OWNERSHIP … PASS` |
| 已有流水/区块/实体路径 | 600000 次缺失读取、400000 次已加载读取、必须生成请求安全失败、实体路由至多一次、真实水流扩散 | 四个 `… REGRESSION PASS`,其中包括 `INTERACTION REGRESSION PASS` |
| 空载/无可选模组 | 不安装上述可选模组仍启动;100 次空 drain 没有新增批次 | `PAYLOAD IDLE PASS` 及通用 payload 标记 |
真实把手的失败证明不是“函数调用成功”:测试让维度线程持有 plot 世界归属,直接运行原松手包后物理约束仍然 `isValid=true`;相同真实包经公共安全入口运行后 `isValid=false`。测试使用 FakePlayer(有真实测试监听器),并非真人客户端输入或真实网络连接。
四个审查问题均先失败后通过:业务拒绝异常误触发踢人、动态循环丢失菜单暂停、退出中的写入者漏过屏障、停服清理后晚到诊断重新持有服务器。日志 `payload-review-red-2.log` 对应失败,`payload-review-final.log` 对应修正后的通过。
诊断分类额外使用 `payload-counter-red.log` 复现“业务失败与容量拒绝混计”,`payload-counter-green.log` 通过;`payload-candidate-real.log` 再次通过真实模组测试并检查换世界旧任务计入 expired。failed、cancelled、expired、rejected 分开统计。
旧交互回归第一次运行失败,不能因 Gradle 返回零而算通过:旧夹具没有 connection,且仍要求已经替换掉的单维度桥。夹具改为 FakePlayer 与公共独占断言,保留所有流水/区块检查,并补上日志 PASS/异常检测门禁;`payload-final-interaction-green.log` 才是通过记录。
## 本轮取舍
1. Windows 无 bash,以 PowerShell/apply_patch 维护执行记录;风险是记录遗漏,测试门禁不变。
2. 主线程独占标志与菜单暂停分离;否则嵌套 resume 会提前放走维度线程。代价是多一组生命周期状态。
3. 阶段、入口、生命周期共用夹具及文件,相关提交合并验证,不把中间状态当发布版本。
4. 保留 NeoForge 异步业务异常返回 null 的 guard 行为,但替换无限逐条异常打印为有界诊断。显式策略失败(旧玩家/停止/溢出)保留异常 future,执行前取消传播到底层队列;这是有意的兼容语义细化,依赖“失败也返回 null”的调用方可能观察到变化。
5. 普通主线程同步调用为保序与同步完成可连续处理多个有界批次;2 ms 不是整 tick 硬上限,慢维度和慢处理器仍可能拉长等待。独占阶段内嵌套立即运行。
6. 旧交互夹具适配新连接/归属契约而非放松生产守卫;不能替代真实客户端网络压力验证。
7. 既有停服 30 秒超时、ServerWarashi、C2ME、任意后台世界/光照访问保留为独立问题;它们仍可能造成完整整合包失败。
8. 只交付隔离环境验证的候选,不将以下未测项标成通过。审查没有列出待办 Minor。
## 未完成的验收,不属于已修复声明
- 真人客户端整套飞行、抓握输入、GUI 与完整整合包存档副本测试。
- 两玩家同时抓握、死亡、断线、换维度、拆把手、卸载子世界及溢出拒绝松手后的完整原生约束清理矩阵。已测普通真实松手、通用旧世界拒绝和溢出断线调用,不能互相替代。
- 跨服务器实例隔离的双实例并行压力测试;实现按服务器分区,不能仅凭代码视为此项实测。
- 空载、普通交互、密集抓握、多人查询和慢维度的完整对比压测。空队列无批次及慢 worker 等待机制已验证,不承诺开销比例或全服 20 TPS。
- 任意 worker 提交后同步 join 主线程队列不受支持。夹具通过 250 ms 超时检测并取消这种等待环,不表示生产代码会自动化解任意 join 死锁。
- ServerWarashi 分析立即崩溃仍需单独从采样栈修复。
## 重跑
以下命令均使用 `--no-daemon --no-configuration-cache --console=plain`,先检查隔离世界配置,不能指向正式存档:
```powershell
.\gradlew.bat check
.\gradlew.bat runServer -PpayloadRegression
.\gradlew.bat runServer -PpayloadRegression -PreviewRegression
.\gradlew.bat runServer -PpayloadRegression '-PpayloadModsDir=<本机上述模组目录>'
.\gradlew.bat runServer -PownershipRegression '-PccTestJar=<本机CC jar>'
.\gradlew.bat runServer -PinteractionRegression
.\gradlew.bat clean build
```
每组运行必须完成后再启动下一组。查看对应 PASS 标记及服务器异常,而非只看 Java/Gradle 退出码。正常构建不可带任何回归夹具属性。
## 打包门禁
`check runServer -PpayloadRegression` 成功(五个纯 Java 回归程序及无可选模组服务器夹具);随后不带任何夹具属性的 `clean build` 成功,五个纯 Java 回归再次执行通过。真实模组、CC、交互及审查运行见上述分组,未将测试标记缺失的运行计入通过。
发布 jar:`build/libs/dimensionalripper-1.1.17.jar`,277211 字节。ZIP 内容检查确认元数据版本为 1.1.17,包含公共入口 mixin/执行器,不含 Regression、FakePlayer、tests 或打包的可选模组 jar/类。
SHA256:`CF1DE863DE119272098142CF5F0ABE78543BA55B15074369A1A66FBC922FBAD3`。
完整本地日志已保存在 `build/reports/payload/`(忽略提交)。关键最终文件:`payload-clean-build.log`、`payload-candidate-check.log`、`payload-candidate-real.log`、`payload-counter-green.log`、`payload-final-cc.log`、`payload-final-interaction-green.log`。失败前例也保留供对照。执行工作区仍保留未完成验收记录,不把剩余矩阵标为完成。
@@ -0,0 +1,133 @@
# 公共收包线程归属 Implementation Plan
> **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:** 将受支持的服务端游戏包交给真正主线程独占处理,修复把手松开路径,同时保留阶段之外的维度并行。
**Architecture:** 每服务器有界 FIFO 接收显式主线程工作;服务器外层 tick 建立全维度安全阶段,批量调用原处理器。阶段状态阻止递归收口和新增维度写入,旧组装入口复用同一执行权。生命周期清理与通用包失效分开处理。
**Tech Stack:** Java 21、Minecraft 1.21.1、NeoForge、Mixin/MixinExtras、现有 Gradle 隔离服务器回归。
**Spec:** `docs/superpowers/specs/2026-10-06-payload-ownership-design.md`
**执行状态(2026-10-06):** Task 1 已提交 `691e949`;Task 2–5 的公共入口、阶段、适配与诊断代码已实施并有回归证据。Task 4 的完整原生资源异常清理矩阵、Task 5 的双服务器压力测试、Task 6 的完整性能/客户端矩阵未全部验收,因此不勾选混合这些要求的条目。具体通过项和剩余项以 `docs/payload-ownership-1.1.17-validation.md` 为准,本轮只交付候选包。
## Global Constraints
- 第一版新公共入口默认全维度隔离,不凭“玩家当前维度”或包内坐标猜测整个处理器的访问范围。
- 初始批次上限为 64 项或 2 ms 的处理时间,达到任一条件后不启动下一项;不强行中断已开始的处理器。
- 服务器队列上限 4096 项、单连接上限 256 项。
- 记录上限 128 个 ID,超出归入 other,避免恶意 ID 撑大统计。
- 登录、握手、配置阶段、客户端接收和纯网络线程处理不被无条件改派。
- 超时用于诊断,不是允许并发执行的理由。
- 不自动上传远端或部署正式服务器。测试仅操作仓库隔离世界或显式创建的存档副本。
- 不改变原处理器权限、距离、强加载规则;不放松 Level 的线程守卫。
- ServerWarashi 是后续独立修复,需要单独的采样设计及失败测试;本计划不宣称解决该崩溃。
- C2ME、后台光照查询及既有停服超时不纳入修复声明。
## Review Focus
- 主线程正在服务等待中的 Sable 回调时再次 enqueue:不得等待该回调所属 worker,任务 2、3 验证。
- 前一个包传送/替换玩家后,同连接后续旧包不得操作新世界:任务 3、4 验证。
- 嵌套调用导致新世界/循环出现:不得在独占期间启动另一个写入者,任务 2 验证。
- 队列满载时恰好收到松手:拒绝要显式完成 future,断线清理不能遗留约束,任务 1、4 验证。
- 停服与阶段 finally、开关/菜单暂停同时发生:不得恢复已停止或仍应暂停的循环,任务 2、5 验证。
## 执行与证据约定
当前工作树含尚未提交的 1.1.16 归属修复,不能用新 worktree 的裸 HEAD 替代这些依赖。执行开始按 worktree 技能检查隔离方式;保留并核对所有现有变更,禁止 reset 或覆盖。不把原有未验证内容顺手算作本次通过。
以下路径相对仓库根。每个任务先写断言、记录失败,再改生产代码;每任务结束显式暂存该任务文件并提交(git 使用本仓库 safe.directory)。混合文件先核对 diff,不能误纳入用户后续改动。
命令统一使用 Java 21、`--no-daemon --no-configuration-cache --console=plain`。新的 `-PpayloadRegression` 夹具与 `ownershipRegression`、`interactionRegression` 互斥;运行结束必须有全部指定 PASS 标记且没有 unexpected exception,不能仅依赖进程退出码。证据放 `build/reports/payload/`。
## 文件职责
- `core/compat/PayloadWorkQueue.java`:不依赖 Minecraft 的 FIFO、容量、取消及批次预算。
- `core/compat/MainThreadPhaseCoordinator.java`:服务器阶段状态与独占租约。
- `core/compat/PayloadMainThreadDispatch.java`:实际玩家上下文、future、生命周期与 drain。
- `core/compat/PayloadDiagnostics.java`:有界计数及耗时快照。
- `mixin/ServerPayloadContextMixin.java`:两个 enqueueWork 重载,保留原有分支及 guard。
- `mixin/MinecraftServerPayloadMixin.java`:外层 tick 的 drain,不扩展到通用 pollTask。
- 已有 ServerManager、LevelTickLoop、Sable 桥、组装兼容、停止入口:接入阶段生命周期。
- `tests/PayloadWorkQueueRegression.java`:纯 Java 队列测试。
- `tests/payload/PayloadRegression.java`:实际 NeoForge 上下文与多维度集成。
- `tests/payload/SimulatedHandleRegression.java`:可选真实模组处理器及约束清理测试。
## Task 1: 可测试的有界队列
**Files:** 创建 `src/main/java/dev/modzuozhi/core/compat/PayloadWorkQueue.java`、`tests/PayloadWorkQueueRegression.java`;修改 `build.gradle`。
**Interfaces:** `PayloadWorkQueue<K>(LongSupplier nanoTime)`;`<T> CompletableFuture<T> submit(K connection, Supplier<T> action)`;`int drainBatch()`;`void close(Throwable reason)`;`int size()`。拒绝返回异常 future;执行前已取消的任务不产生副作用。锁只保护队列及计数,绝不包住 action 或 completion 回调。
- [ ] 写 `fifoAndExactlyOnce`、`capacityAndCancellation`、`batchCountAndTime`、`closeAndReentrantCompletion` 断言:同连接顺序 0..255;第 257 项拒绝;16 个连接各 256 项填满后第 4097 项拒绝;取消使容量回收;批次最多 64;假时钟到 2_000_000 ns 后不再启动下一项;慢 action 不被中断;close 后旧 future 全结束且不能再接收。
- [ ] 添加 `runPayloadWorkQueueRegression` 到 check;先运行并保存失败证据,不能以无测试发现作为 RED。
- [ ] 实现上述接口;future 完成回调可重新 submit/close 而无锁死;Supplier 异常只影响本项。
- [ ] 运行 `./gradlew.bat runPayloadWorkQueueRegression`,输出 `PAYLOAD QUEUE PASS`;并行提交/取消压力测试核对计数为零。
- [ ] 审阅并提交本任务文件。
## Task 2: 安全阶段与循环生命周期
**Files:** 创建 `src/main/java/dev/modzuozhi/core/compat/MainThreadPhaseCoordinator.java`、`tests/payload/PayloadRegression.java`;修改 `core/dimthread/ServerManager.java`、`core/dimthread/LevelTickLoop.java`、`core/compat/SableMainThreadBridge.java`(均位于 `src/main/java/dev/modzuozhi/`),修改 `build.gradle`。
**Interfaces:** coordinator 的 `void run(MinecraftServer server, String reason, Runnable action)`、`boolean owns(MinecraftServer server)`、`boolean blocksWorkerStart(MinecraftServer server)`、`void stopping(MinecraftServer server)`、`void clear(MinecraftServer server)`;Sable 桥新增 `boolean inHandoff(MinecraftServer server)`。ServerManager.runMainThreadPhase 委托 coordinator,保持现有 CC 调用不变。
- [ ] 写 `exclusivePhase`、`nestedPhase`、`handoffReentryRejected`、`newLoopDuringPhase`、`stopDuringPhase`:处理期间全部世界 owner 为真主线程、worker 写入计数不增加;等待可完成 Sable 回调;独占内嵌套只建一次屏障;handoff 中建全局屏障立即明确失败;新 loop 在阶段退出前不能开跑;预先暂停、菜单暂停、停止状态不被 finally 恢复。
- [ ] `./gradlew.bat runServer -PpayloadRegression` 得到预期断言失败,保存日志;夹具另用超时终止测试而非让生产代码越过屏障。
- [ ] 实现 IDLE/DRAINING/EXCLUSIVE/RESTORING/STOPPING 状态、租约与入口检查。循环注册、scheduleIfNeeded、resume 共享阶段启动门禁;新 loop 参与退出处理。等待不持锁,继续 pump 必要回调;嵌套 drain 不递归。循环已经在途时等待其子任务及线程字段全部归还。
- [ ] 运行夹具输出 `PAYLOAD PHASE PASS`;复跑 ownershipRegression,确认 CC 预置暂停及 Sable 回调仍通过。
- [ ] 审阅并提交本任务文件。
## Task 3: NeoForge 公共入口与 future 语义
**Files:** 创建 `src/main/java/dev/modzuozhi/core/compat/PayloadMainThreadDispatch.java`、`src/main/java/dev/modzuozhi/mixin/ServerPayloadContextMixin.java`、`src/main/java/dev/modzuozhi/mixin/MinecraftServerPayloadMixin.java`;修改 `tests/payload/PayloadRegression.java`、`src/main/resources/dimensionalripper.mixins.json`。
**Interfaces:** dispatch 的 `<T> CompletableFuture<T> submit(ServerPlayer player, ResourceLocation payloadId, Supplier<T> action)`、`boolean owns(MinecraftServer server)`、`void drain(MinecraftServer server)`、`void stop(MinecraftServer server)`。Runnable 适配 Supplier<Void>。任务保存实际 connection、player、接收时 ServerLevel 身份及时间;各服务器实例独立。
- [ ] 用真实 ServerPayloadContext 的 Runnable/Supplier 路径写 `mainPayloadSeesBlockEntity`、`futureContract`、`phaseEscapeAndStalePlayer`、`configurationPassthrough`。断言并发维度下能取得原 BE、true main、至多一次;Supplier 返回 42;异常、取消和 guard 的对外结果与未修改 NeoForge 一致,内部真实失败仍可见;权限拒绝未发生世界变更;配置阶段不调用 player();网络计算原线程不变。
- [ ] 运行 payloadRegression 记录原入口 wrong-owner/null 的失败;记录尚未覆盖的真实 Veil 路径,不冒充实测。
- [ ] 两个 enqueueWork 仅拦截游戏玩家的显式主线程工作。网络/worker 投递不等待;独占内嵌套立即完成;普通主线程外层进入安全阶段时先遵守同连接已排队顺序;DRAINING 中普通投递只入队;同步 handoff 重入按任务 2 契约明确失败。原 handler、上下文与 NetworkRegistry.guard 保留,不双重执行。
- [ ] 在 MinecraftServer.tickServer HEAD 的外层主线程 drain 一批;不在 pollTask 或 worker 回调内 drain。执行前验证连接、player 身份及世界;失效 future 明确结束。无队列不建屏障。
- [ ] 加 `workerWaitCycleDetected` 测试:worker 等待队列 future 时测试必须识别无法收口且取消夹具,不误报自动兼容。运行集成输出 `PAYLOAD ENTRY PASS`,覆盖没有并行 loop 的原版情况。
- [ ] 审阅并提交本任务文件。
## Task 4: 原模组交互与清理路径
**Files:** 修改 `src/main/java/dev/modzuozhi/core/compat/SableAssemblyDispatch.java`、`src/main/java/dev/modzuozhi/mixin/CreateConfigurationPacketMixin.java`、`src/main/java/dev/modzuozhi/mixin/SimulatedAssemblePacketMixin.java`;创建 `tests/payload/SimulatedHandleRegression.java`;修改 `build.gradle`。若真实路径证明需要专用适配,仅新增 `src/main/java/dev/modzuozhi/mixin/JadeRequestDispatchMixin.java` 与 `src/main/java/dev/modzuozhi/mixin/SimulatedHandleLifecycleMixin.java`,并登记可选 mixin。
**Interfaces:** 保持 `SableAssemblyDispatch.dispatch(ServerPlayer,Runnable)`,改为复用公共阶段/投递,禁止再送进已暂停的维度队列。具体可选模组方法描述符从本地实际 jar 审计后锁定,不能猜注入目标。
- [ ] 先记录真实 `UpdatePlayerUsingHandlePacket.handle` 的抓取、距离、松手入口、BE 身份、HandleConstraint 移除结果,建立 RED。核对 HandleBlockEntity.checkPlayers 及断线/死亡/卸载清理,不仅测试模拟 Runnable。
- [ ] 审计 Veil 注册和 Jade 二次 server.execute;建立逃逸测试。只有已观察的逃逸点使用定点包装,不改写全部 MinecraftServer.execute。旧 Create/组装入口在公共独占内直接调用原 handler;阶段外走相同队列。
- [ ] 测试顺序与副作用:抓取一次、更新距离一次、松手一次,最后 constraint 不存在;两个玩家不会互相释放;转速配置、组装拆解保留原权限和距离行为,错误坐标不强加载。
- [ ] 覆盖断线、换维度、死亡、把手拆除、sublevel 卸载和单连接 256 满载后松手被拒绝;若原清理不足,可信生命周期适配在旧世界安全阶段释放资源,不重放过期客户端包。过载断开连接调用原 disconnect/cleanup,日志限频。
- [ ] 真实运行输出 `SIMULATED HANDLE PASS`、`CREATE CONFIG PASS`、`JADE REQUEST PASS`。原模组依赖或原生物理无法启动时,记录未通过项并暂停“把手已修复”的交付结论,不能用假夹具替代。
- [ ] 审阅并提交已验证的适配文件;无必要不创建专用 mixin。
## Task 5: 停止、可观测性与限频
**Files:** 创建 `src/main/java/dev/modzuozhi/core/compat/PayloadDiagnostics.java`;修改 `src/main/java/dev/modzuozhi/core/compat/PayloadMainThreadDispatch.java`、`src/main/java/dev/modzuozhi/mixin/MinecraftServerDimMixin.java`、`src/main/java/dev/modzuozhi/core/compat/ThreadOwnershipDiagnostics.java`、`src/main/java/dev/modzuozhi/command/MasterCommand.java`、`tests/payload/PayloadRegression.java`。
**Interfaces:** `PayloadDiagnostics.snapshot(MinecraftServer server): String`,只读且不新建循环;dispatch.stop 拒绝接收、终结待执行 future,阶段退出不恢复停止中的 loop;clear 在清理结束移除服务器引用。
- [ ] 写 `boundedDiagnostics`、`stopRejectsAndReleases`、`twoServersIsolated`:1000 个不同 ID 最多 128 个具名项加 other;1000 次失败不输出1000份堆栈;停止后无遗留 future/连接/世界引用;某一服务器 stop 不影响另一服务器。
- [ ] 运行得到 RED。实现每服务器计数、队列最老等待、最近 32 事件、等待/执行耗时、批次数;异常全局 30 秒限频,快照无包正文。正常/异常停止入口在既有循环停止与保存之前关闭新队列;不要把旧 30 秒停服风险说成已解决。
- [ ] status 和 crash report 附快照,区分等待 worker、handler 慢和 Sable handoff;测试所有失败 future 最终完成且 handler 从不重复执行。
- [ ] 运行 payloadRegression 输出 `PAYLOAD LIFECYCLE PASS`、`PAYLOAD DIAGNOSTICS PASS`,审阅并提交。
## Task 6: 兼容验收与候选包
**Files:** 修改 `README.md`、`gradle.properties`(候选版本 1.1.17);证据 `build/reports/payload/`。
**Interfaces:** 交付明确列出“源码/字节码审计”“夹具通过”“真实模组通过”的覆盖表,不将它们合并为兼容保证。
- [ ] 分别运行 `./gradlew.bat check`、`runServer -PpayloadRegression`、`runServer -PownershipRegression -PccTestJar=<已验证本地CC路径>`、`runServer -PinteractionRegression`。逐项确认纯 Java、CC、区块、水流、实体路由和新 PASS 标记,不把预期故障夹具当成功事故。
- [ ] 无 Create/Veil/Jade/Simulated 可选模组启动通过;真实模组组合另跑,不自动复制到用户 mods 目录。
- [ ] 对比无交互、普通交互、密集抓握、多人查询及慢维度:主线程 TPS、维度完成 TPS、队列延迟、阶段耗时。无交互新增屏障数必须为零;报告慢维度代价,不以 Carpet MSPT 独立判定。
- [ ] 正常构建 `./gradlew.bat clean build`,不带任何夹具属性。检查 jar 无 tests/Regression/测试模组类、元数据 1.1.17,记录 SHA256。
- [ ] 按 requesting-code-review 技能审查完整差异,修复阻塞问题并重跑受影响测试。README 清楚标注 ServerWarashi 仍未由本计划修复以及真实航空学验收状态;提交本任务。
- [ ] 只交付本地候选 jar 和覆盖/风险说明;如真实物理未通过,交付测试候选而非宣称全部修好。不推远端、不部署。
## 自审结果
设计第 4 节对应任务 1–3;旧适配、Veil/Jade 与生命周期对应任务 4–5;有界诊断与性能/打包对应任务 5–6。所有五个 Review Focus 已映射到断言。ServerWarashi 明确作为独立后续工作,不以当前方案替代采样栈修复。
@@ -2,7 +2,7 @@
日期:2026-10-06(Asia/Shanghai)
状态:用户已同意“共用入口、正确性优先”的方向;本文待审阅,尚未进入实施。
状态:用户于 2026-10-06 批准直接实施;公共入口已实现并进行候选验证。完整验收尚有未测项,见 `docs/payload-ownership-1.1.17-validation.md`,不得把本文的目标清单当作全部通过。
## 1. 用户目标与边界