diff --git a/docs/plan-refactor-client-store-lifecycle-v1.md b/docs/plan-refactor-client-store-lifecycle-v1.md index 394b29814..118d3f914 100644 --- a/docs/plan-refactor-client-store-lifecycle-v1.md +++ b/docs/plan-refactor-client-store-lifecycle-v1.md @@ -24,7 +24,7 @@ - **模块 / symbol**:`client/src/main/java/com/bong/client/lifecycle/{ClientStoreScopeManifest,SessionScopedStore,SessionStoreHandle,SessionScopedStoreRegistry}.java`;`SessionScopedStore.clearOnDisconnect()`;`SessionStoreHandle.forStore(...)`;`SessionScopedStoreRegistry.clearAllOnDisconnect()`。 - **测试抓手**:`ClientStoreScopeManifestTest` 精确 pin 108 个 Store 的三分类、106 个 session Store、`ClientConnectionStatusStore` 外部 token 管理与 P0 空 registry;`SessionScopedStoreRegistryTest` pin 声明顺序、重复 FQCN fail-fast、Store / reporter 异常隔离和 `Error` 透传。 - **跨仓库契约**:纯 client 生命周期基础设施;schema、Redis key、CustomPayload 均无新增或变更。 -- ⬜ **P1 在册 Store 平移**:把当前 `clearClientStateOnDisconnect()` 中已存在的 Store 清理逐项迁入 registry;断线 helper 改为调用 registry 一次。非 Store 的 renderer / handler / ambience 等生命周期 hook 继续由 helper 显式拥有,P1 不借重构删除它们。 +- ⬜ **P1 在册 Store 平移**:把当前 `clearClientStateOnDisconnect()` 中已存在的 Store 清理逐项迁入 registry;断线 helper 改为调用 registry 一次。非 Store 的 renderer / handler / ambience 等生命周期 hook 继续由 helper 显式拥有,P1 不借重构删除它们。R1 craft 接缝以本阶段已登记的 `CraftStore` 为唯一 client session-state owner;R7 P2 只消费该 Store 实现 close/pause/reopen/resume,不再建立第二份状态。 - **模块 / symbol**:`client/src/main/java/com/bong/client/lifecycle/SessionScopedStoreRegistry.java` 的显式 `REGISTERED`;`client/src/main/java/com/bong/client/BongNetworkHandler.java` 的 `disconnectSession(...)` / `clearClientStateOnDisconnect()`。 - **测试抓手**:`ClientStoreScopeManifestTest` 精确 pin P1 已迁移 FQCN 集;`BongNetworkHandlerTest` pin token invalidation 先于 registry、迟到旧 handler 不清新 session、非 Store hook 仍保留;每个 adapter 以目标 Store 的状态级行为测试证明 method reference 未错绑。 - **跨仓库契约**:保持现有 wire、schema、Redis key 与 CustomPayload 不变。 @@ -111,7 +111,7 @@ skeleton:niche-guardian-cross-session-leak。 - 独占:全部 `*Store.java` 的生命周期接口与登记、`BongNetworkHandler.java` 的 `clearClientStateOnDisconnect` 区段。 - 不碰:`BongNetworkHandler.register()` 的 channel 注册区(R6 域,同文件分区段协作,两轨 merge 前互相 fetch);Screen 结构(R7 域);store 的业务字段语义。 -- 依赖:无前置,Wave 0 即可动工。R7/R9 依赖本轨接口,先于它们合入。 +- 依赖:Wave/start/order/cutover 只引用 master §3/§4.1 与 PR 1902;R7/R9 只消费本轨冻结的 Store interface,不在 R2 复制跨轨箭头。 ## bot 验收场景 diff --git a/docs/plan-refactor-inventory-core-v1.md b/docs/plan-refactor-inventory-core-v1.md index d261a58d8..91cf5e19e 100644 --- a/docs/plan-refactor-inventory-core-v1.md +++ b/docs/plan-refactor-inventory-core-v1.md @@ -4,13 +4,13 @@ ## 阶段 -- ✅ 2026-08-03 P0 完整契约面重写 + absorption audit -- ⬜ P1:inventory 拆分 + txn/capacity 骨架 + inventory-layout/dropped-loot 纯 migration helpers(依赖 R3 P1 seam) -- ⬜ P2:production writer 迁移分为 P2a metadata/provider + Public writer path 与 P2b OwnerOnly private-writer activation;P2a 依赖 R3 P2 atomic commit seam 与旧 dropped-loot compatibility,P2b 必须等 R3 P4 dropped-loot migration/hydration、R6 P1 recipient projection/page、R5 P3 + R6 P4、R10 P3 pickup txn 及 R4 pickup consumer 全部完成后才可启用。 -- ⬜ P3:pickup/merge txn(依赖 R5 P3 attrition API、R6 P4 receipt API) -- ⬜ P4:联合 bot/e2e + plan 收口(依赖 R4 handler 与 R3 P4 legacy inventory-layout consumer) +- ⏳ P0 完整契约面重写 + absorption audit +- ⬜ P1:inventory 拆分 + txn/capacity 骨架 + inventory-layout/dropped-loot 纯 migration helpers;对应 master M-06/M-13 的 transaction/provider surface,R3 seam 只按 M-04 提供。 +- ⬜ P2:production writer 迁移分为 metadata/provider、Public/OwnerOnly writer 与 terminal worker;terminal worker 对应 M-06/O-10..O-27,dropped-loot hydration/projection 与 pickup consumer 只按 master M-13/M-14/M-15 启用。 +- ⬜ P3:pickup/merge txn;R4/R5/R6 consumer 与 receipt/attrition 接缝只引用 master M-14/M-15。 +- ⬜ P4:联合 bot/e2e + plan 收口;完成 evidence 需覆盖对应 M-row 与 canonical O-row。 -实现属 Wave 2;跨轨工作须登记 owning plan。 +所有跨轨 start/order/cutover 只引用 `plan-refactor-master-v1.md §3/§4.1` 与 PR 1902;本阶段表不复制箭头。 ## 1. 完整 contract surface @@ -39,6 +39,25 @@ InventoryTxn::pickup_and_merge(PickupRequest, PickupAuthorization, &mut DroppedL `consume_checked` receipt 逐 instance 记录扣除量/剩余量;insufficient、unknown、zero 任一失败不得部分扣除。 +### 1.2 Terminal-delivery production consumer(P2c) + +R10 P2c 交付常驻 `SessionDeliveryWorker`,作为 R3 outbox 的唯一 production consumer。它只实现 R1 canonical obligation reducer 的 O-10..O-21/O-26/O-27,不拥有 gameplay session 状态或 teardown: + +```rust +SessionDeliveryWorker::claim_next(now, worker_id) + -> Result, DeliveryWorkerError> +SessionDeliveryWorker::commit_claimed(claimed, Option<&mut SpillContext>) + -> Result +SessionDeliveryWorker::fail(claimed, reason, now) + -> Result +``` + +`commit_claimed` 必须先预留 bounded history capacity(不足走 O-26),再校验 digest并从 `claimed.payload` decode/validate 唯一 `DeliveryRequest`;caller 无权另传 request。O-16 transaction 对拍 canonical bytes/digest、semantic item set 与 inventory receipt,并原子提交 inventory/spill、receipt、obligation delete、quota `-Q`。malformed/digest mismatch 命中 O-14,禁止调用 `deliver`;retry/lease/dead-letter 只走 O-12/O-13/O-18,绝不恢复 R1 session/claim。 + +P2c pins 直接执行 R1 O-10..O-21/O-26/O-27:并发唯一 claim、claim 后 crash/lease expiry、payload A/request B 拒绝、history quota fail-before-mutation、deliver 前后 crash、receipt replay、retry/dead-letter/operator CAS、满包 spill。R1 `session_delivery_crash_atomicity` 与 R3 `session_delivery_outbox_atomicity` 必须走真实 worker,不得由 domain 直接调用 `InventoryTxn::deliver` 冒充 consumer。 + `pickup` receipt 含 request id、revision、removed drop、merge/placement、`target_instance_id`、`incoming_instance_id/count/abs_qi_before`。placement 的 target 等于 incoming;merge 的 target 是提交后既有 stack。R5 只按 incoming absolute qi 做 attrition:`target_after = preexisting_abs_qi + incoming_after`,不得磨损旧数量或由合并后整栈反推。 ## 2. Spill / overflow 守恒 @@ -87,15 +106,10 @@ R10 的 inventory-layout 与 dropped-loot migration 函数均纯且幂等,保 ## 6. 所有权与顺序 -- **R10**:`server/src/inventory/**` model/grid/txn/capacity、writer enumeration、typed outcome、纯 migration。P1 仅在 **R3 P1** 的 inventory/overflow seam 冻结后实现 txn/capacity 骨架;P2a 负责 metadata/provider 与 `Public` writer path,依赖 **R3 P2** durable spill/pickup recoverable-commit seam、旧 dropped-loot migration compatibility 与 crash/retry pins;P2b 才能启用 `OwnerOnly` private writers,且必须等 **R3 P4 dropped-loot migration/hydration、R6 P1 recipient projection/page、R5 P3 + R6 P4、R10 P3 pickup txn、R4 pickup consumer** 全部完成;P3 pickup/attrition consumer 只有在 **R5 P3** incoming-only attrition/ledger API 与 **R6 P4** receipt wire/client API 已合入后才可接通。 -- **R3**:SQL/outbox、spill/pickup recoverable commit、hydration guard、migration consumer;R10 只消费 R3 P1/P2/P4 已冻结并实现的接口。P4 必须拆成 dropped-loot hydration 子批次与 inventory-layout overflow 子批次,前者不等待 R10 P3,后者才等待其实际 durable/capacity 前置。 -- **R4**:C2S gate/handler、authoritative pickup context、调用 R10 并转交 R6 outcome;R4 handler/consumer phase 必须等待 **R10 P3 pickup txn、R6 P4** receipt API 与 **R5 P3** attrition API,不得以 R10 mock 或仅 R6 P1 schema 代替。 -- **R5**:incoming-only qi attrition/ledger;provider phase 为 R5 P3。 -- **R6**:receipt wire/client、recipient projection/page、decoder;canonical plan 登记 rotate、pack feedback、dropped sync;dropped-loot projection/page consumer 为 R6 P1,必须在 **R10 P2a owner/visibility metadata provider** 与 **R3 P4 dropped-loot migration/hydration consumer** 完成后才可消费;receipt provider phase 为 R6 P4。 -- **R1**:txn stored/spilled 成功后才 teardown,失败保留 session。 -- **R7**:UI 消费,不拥有事务。 - -顺序:**R3 P1 → R10 P1(含纯 inventory-layout/dropped-loot migration helpers)→ R3 P2 atomic seam 实现 + legacy dropped-loot migration/hydration compatibility pins → R10 P2a metadata/provider + Public writer path → R3 P4 dropped-loot migration/hydration consumer → R6 P1 dropped-loot projection/page consumer → R5 P3 + R6 P4 → R10 P3 pickup/merge txn → R4 handler/pickup consumer → R10 P2b OwnerOnly private-writer activation → R3 P4 inventory-layout overflow consumer → R10 P4 联合 e2e**。R10 P2a 在 R3 P2 atomic seam 与旧 dropped-loot migration compatibility 未合入前不得开始;R3 P4 dropped-loot migration/hydration consumer 必须先于 R6 P1 dropped-loot projection/page consumer,确保旧 `entry_json` 已先升级为带 `owner`/`visibility` 的 canonical entry;R10 P2b 只有在 R3 P4 dropped-loot hydration、R6 P1 projection/page、R5 P3 + R6 P4、R10 P3 pickup txn 与 R4 pickup consumer 全部完成后才可启用,避免 OwnerOnly writer 在授权消费链闭合前广播或转移私有掉落;R10 P3 在 R5 P3/R6 P4 provider 未合入前不得开始 pickup consumer;R4/R3 的外轨交付物只作 R10 P4 验收前置、不计入 R10 自身 phase;R10 不越权改 persistence、wire、handler 或 client。 +- **R10** 独占 `server/src/inventory/**`、纯 migration helpers 与 `SessionDeliveryWorker`;terminal worker 只投影 R1 O-10..O-21/O-26/O-27,inventory/spill transaction 不定义 session teardown。 +- **R3** 独占 SQL/outbox/CAS/reconciliation、hydration guard 与 migration consumer;R10 通过冻结接口 claim/commit/fail。 +- **R4/R5/R6/R7** 分别拥有 C2S authorization、qi attrition ledger、receipt/projection wire 与 UI consumer;R10 不修改其文件。 +- 所有跨轨 start/order/cutover 仅引用 master §3/§4.1 与 PR 1902,不在本 plan 复制箭头。接口可 contract-first 合入;真实 worker activation 必须等 master 列出的 R3 outbox 与 R10 transaction artifacts 存在,且不得用 mock 宣称 production closure。 ## 7. 审核要求的 contract pins @@ -108,15 +122,16 @@ R10 的 inventory-layout 与 dropped-loot migration 函数均纯且幂等,保 5. pickup 同维成功;跨维、超距/zone、owner/private 拒绝;merge、placement-only、failed attach/capacity/validation/persistence 后 entry 仍在;成功后才删。 6. incoming-only attrition receipt + R5 ledger:旧 stack absolute qi 不变;注入 attrition 后、durable commit 中断与 restart/retry,断言 attrited item + zone/ledger + drop delete 原子且总量守恒。 7. visibility matrix:同维/范围内 `Public` 对非 owner 可见,`OwnerOnly` 对 owner 可见、对普通非 owner 不可见、对 server-authorized admin 可见;另测跨维/超距拒绝。page/revision 按每个 recipient projection;缺页/混 revision 不替换。 -8. accepted/rejected move correlation;pack stow/equip/unequip 与拒绝必须动作级 receipt,stale event 和 snapshot-only baseline 不通过。 -9. forge 深链保留;另锁 `/give hoe_iron → 新 snapshot → 真实非零 instance → held/equip → lingtian_start_till`,禁止 `instance_id=0` 或任意 server-data 冒充成功。 -10. inventory-layout migration pure happy/empty/full/dynamic/idempotent/invalid;dropped-loot migration 覆盖旧 `entry_json` 缺 owner/visibility → `None`/`Public`、已有字段原样保留、malformed/幂等;R3 consumer 对真实 context 成功,缺 context/capacity/persistence/migration failure 保留旧行可重试。 +8. terminal-delivery worker:执行 R1 O-10..O-21/O-26/O-27,覆盖空队列、并发唯一 claim、lease expiry、payload/digest binding、history quota fail-before-mutation、commit crash、receipt replay、retry/dead-letter/operator CAS;quota effect 逐 row 对拍。 +9. accepted/rejected move correlation;pack stow/equip/unequip 与拒绝必须动作级 receipt,stale event 和 snapshot-only baseline 不通过。 +10. forge 深链保留;另锁 `/give hoe_iron → 新 snapshot → 真实非零 instance → held/equip → lingtian_start_till`,禁止 `instance_id=0` 或任意 server-data 冒充成功。 +11. inventory-layout migration pure happy/empty/full/dynamic/idempotent/invalid;dropped-loot migration 覆盖旧 `entry_json` 缺 owner/visibility → `None`/`Public`、已有字段原样保留、malformed/幂等;R3 consumer 对真实 context 成功,缺 context/capacity/persistence/migration failure 保留旧行可重试。 ## 8. Named bot acceptance(P4) 以下名称即 `scripts/bot/scenarios/.py` 的稳定身份: -1. `inv_full_delivery_matrix`:craft/alchemy/forge/give 满包时 `stored + spilled == requested`,失败不 teardown。 +1. `inv_full_delivery_matrix`:craft/alchemy/forge/give 满包时 `stored + spilled == requested`;checkpointed 三域经真实 `SessionDeliveryOutbox`→P2c O-10..O-16→receipt 链。handoff 已在 R1 S-14 teardown,worker failure 只留 Pending/DeadLetter obligation,不恢复 session。 2. `inv_stack_merge`:同 identity merge、异 identity 分栈;placement-only 与拒绝路径保留 drop;attrition durable 中断/restart 仍原子守恒。 3. `inv_footprint_sync`:2×1 rotate 后以 request/instance/from/to/revision 锚定 1×2 authoritative receipt;snapshot 不代替回执。 4. `inv_pack_feedback`:stow/equip/unequip 的 accepted/rejected 均按时间锚与 correlation 匹配,stale event 不通过。 @@ -128,7 +143,7 @@ R10 的 inventory-layout 与 dropped-loot migration 函数均纯且幂等,保 | plan | 2026-08-03 验真结论 | 证据/落点 | |---|---|---| -| `alchemy-takeback-full-inventory-loss` | 部分吸收 | 满包 deliver/spill 后才由 R1 teardown。 | +| `alchemy-takeback-full-inventory-loss` | 部分吸收 | terminal handoff 后由 obligation worker deliver/spill;失败走 O-13,不 reopen。 | | `dropped-loot-pickup-stack-merge` | 仍真实,吸收 | 当前 pickup 只找空 footprint;落 `pickup_and_merge`。 | | `force-attach-grid-collision` | 仍真实,吸收 | `(0,0)` 强塞仍可达;改合法 attach/spill。 | | `rotate-footprint-sync` | 仍真实,吸收 | `Moved` 缺权威新 footprint;R6 receipt 补齐。 | diff --git a/docs/plan-refactor-server-session-v1.md b/docs/plan-refactor-server-session-v1.md new file mode 100644 index 000000000..a1a5265e4 --- /dev/null +++ b/docs/plan-refactor-server-session-v1.md @@ -0,0 +1,210 @@ +# plan-refactor-server-session-v1 — Server 交互 Session 生命周期与终态交付(重构轨 R1) + +> 所属总纲:`plan-refactor-master-v1.md`。本轨唯一负责 gameplay session;终态交付的 durable 语义由本文件的 canonical contract 定义,R3/R10 只实现其存储与消费投影。跨轨排序引用总纲 §3 及 PR 1902 五项裁决,不在本文件另建依赖图。 + +## 阶段总览 + +| 阶段 | 交付物 | 状态 | +|---|---|---| +| P0 | 现状/吸收验真;canonical session + obligation reducer;row-ID acceptance index | ⏳ | +| P1 | `server/src/session/` 框架与 craft contract/adapter 接缝 | ⬜ | +| P2 | alchemy、forge、lingtian adapter | ⬜ | +| P3 | gathering、mineral、spiritwood、世界交互 adapter | ⬜ | +| P4 | derived bot e2e、吸收结案、归档 | ⬜ | + +## 0. 外部决议与边界 + +- 总纲 `plan-refactor-master-v1.md §3/§4` 是跨轨 start/order/cutover 的唯一 authority。本计划引用 master artifact/phase ID,不复制 A-CS→R6→R4→R7→R3→R10 的箭头。 +- PR 1902(`origin/docs/master-r9-r6-ownership-adjudication`,五项 settled rulings)作为外部已定上下文:TypeBox 是全仓 schema source of truth;domain content 与 generation machinery 分权;contract-first 可先合入但不可宣称 live;production activation 必须原子切换;track plan 不得创建第二套 sequencing authority。 +- R1 独占 gameplay session registry、session reducer 和七域 adapter;R3 独占 SQL/checkpoint/outbox storage;R10 独占 inventory/spill transaction 与 worker;R4/R6/R7/R2 各自拥有其 gate/wire/UI/store。任何轨只消费冻结 API,不复制另一轨的状态语义。 +- durable checkpoint 不以 Bevy `Entity` 为主键。`workbench_key` 是当前进程 `WorkbenchOpen.entity_id` 的 runtime locator;checkpoint 只保存 R3 P4 提供的 stable `placed_id`。 + +## 1. 现状与吸收边界 + +### 1.1 七域现状 + +| 域 | 当前权威状态 | R1 结论 | +|---|---|---| +| alchemy | `server/src/alchemy/session.rs:68` `AlchemySession`,挂在 furnace | P2 checkpointed adapter;炉与 session 同一 checkpoint | +| craft | `server/src/craft/session.rs:40` ECS `CraftSession` | P1 首宿主;迁入 registry 后保留既有 hydration 语义 | +| forge | `server/src/forge/session.rs:157` `ForgeSessions` Resource | P2 checkpointed adapter;station/material/session 同一快照 | +| gathering | `server/src/gathering/session.rs:57` store | P3 volatile adapter;中断确定性 teardown | +| lingtian | `server/src/lingtian/systems.rs:196` `ActiveLingtianSessions` | P2 volatile actor adapter;断线停止 tick | +| mineral | `server/src/mineral/session.rs:16` `MiningSession` | P3 volatile adapter;释放矿点/工具 claim | +| spiritwood | `server/src/spiritwood/session.rs:59` store | P3 volatile adapter;`settling` 与 teardown 同步 | + +已确认的接线缺口包括保存前未统一 teardown、跨维残留 owner、TSY Search/Extract 双向 busy 缺失、PendingInsightOffer 无 deadline,以及取回路径先 `end_session` 后 delivery 失败。吸收清单沿用本 PR 原 §4 的逐项验真;被 R2/R3/R4/R10 明确拥有的实现不在 R1 重复登记。 + +## 2. 术语与两个 ownership domain + +R1 不再用一组 `SessionPhase` 同时表示 gameplay 和 delivery。两者通过一次 handoff 连接: + +- `GameplaySession`:`SessionKey`、`PlayerKey`、durability、runtime `Entity` binding、facility/target/busy claim、checkpoint/escrow 和 termination cause。 +- `TerminalObligation`:reservation、quota `Q`、canonical payload、payload digest、delivery lease/retry/dead-letter、receipt/disposition 和 retention metadata。 +- `Q = (1 row, reserved_bytes)`;`quota_used = sum(Q)` 对所有占用 obligation 成立。 +- `workbench_key` 只做当前进程 lookup,不是 authorization、durable identity 或 checkpoint 字段;`placed_id` 才是 restore/rebind identity。 +- `SessionKey`、terminal `generation`、`delivery_id` 和 canonical payload bytes 是 exactly-once 的不同概念:前者定位 session,generation 防旧写入,delivery id 防重复 handoff,payload digest 绑定交付内容。 + +### 2.1 R1 提供的 session 类型 + +```rust +pub trait InteractionSession { + fn session_key(&self) -> SessionKey; + fn owner_key(&self) -> &PlayerKey; + fn durability(&self) -> SessionDurability; + fn busy_claim(&self) -> BusyClaim; + fn reduce(&mut self, event: SessionEvent, ctx: &mut SessionLifecycleCtx) + -> SessionDecision; +} +``` + +固定类型: + +- `SessionDurability::{Checkpointed, Volatile}`;adapter 必须显式注册。 +- `SessionState::{Running, Paused, Suspended, HandoffPreparing, Ended}`。 +- `TerminationCause::{Completed, VoluntaryCancel, Disconnect, DimensionChange, Shutdown, InvalidRestore, SuspensionExpired, AuthorizedAdministratorClosure}`。 +- `BusyClaim`:player-exclusive、target-exclusive、facility-exclusive 三类集中冲突矩阵。 +- `SuspensionPolicy`:`SESSION_SUSPENSION_TTL_TICKS = 1_728_000`、扫描 cadence `1_200` ticks;R3 tick rebase 只保留真实剩余 TTL,不刷新租约。 +- `SessionMaintenancePermissions`:server console,或已认证且绑定当前 executor 的 principal/capability;Username、owner 字符串、offline player 和跨 executor capability 均无权。 + +## 3. Canonical session reducer(唯一 session 规范) + +`reduce_session(state, event) -> {next_state, handoff?, claim_effect, checkpoint_effect, audit_effect}` 是 gameplay 唯一规范。以下 row ID 同时是 acceptance index 的引用;任何 phase prose 必须展开为这些 trace,不得另写 transition。 + +| Row ID | 当前状态 + event | 结果 | claim/checkpoint/delivery effect | +|---|---|---|---| +| S-01 | Absent + admission validation fail | Absent | 无 runtime claim;不创建 obligation | +| S-02 | Absent + `reserve_new_terminal_obligation` 成功且 claim win | Running | obligation O-01→O-03,`+Q`;绑定 owner/target | +| S-03 | Absent + reservation 成功但 busy race-loss | Absent | 执行 O-05→O-07/O-06;只有 O-06 commit 才 `-Q` | +| S-04 | Running + valid pause/close | Paused | claim 保留;不退款、不创建 terminal obligation | +| S-05 | Running + explicit `VoluntaryCancel` | HandoffPreparing | 按域 policy 生成 refund payload;进入 O-08 handoff | +| S-06 | Running + `Completed` | HandoffPreparing | 产物 payload;不 refund inputs;进入 O-08 | +| S-07 | Running + `Disconnect`/`Shutdown` checkpointed | Suspended | 停 tick、写 checkpoint、解绑 runtime Entity;逻辑 facility claim 保留 | +| S-08 | Running + `Disconnect`/`Shutdown` volatile | HandoffPreparing | 全退未消费 escrow;进入 O-08,不可离线推进 | +| S-09 | Running + `DimensionChange` | HandoffPreparing | 停 intake;全退未消费 escrow;不得进入 Suspended | +| S-10 | Suspended + reconnect and guarded restore pass | Paused | `reuse_terminal_obligation`(O-04,`ΔQ=0`);stable `placed_id` rebind | +| S-11 | Suspended + missing/conflicting checkpoint/owner/placed_id | HandoffPreparing | `InvalidRestore`;不得 attach 或覆盖另一 session;进入 O-08 | +| S-12 | Suspended + TTL expiry | HandoffPreparing | `SuspensionExpired`;保留已完成产物交付,未消费 escrow 全退;进入 O-08 | +| S-13 | Suspended + authorized admin closure | HandoffPreparing | `AuthorizedAdministratorClosure`;审计 principal/reason;进入 O-08 | +| S-14 | HandoffPreparing + handoff transaction committed | Ended | 释放 runtime/facility/target claim;obligation 获得唯一 durable payload owner;不可 reopen | +| S-15 | HandoffPreparing + transaction rollback before commit | prior live/checkpoint state | claim/session 保持可重试;不得删除 checkpoint 或扣 quota | +| S-16 | Ended + late gameplay request/reconnect | Ended | stale generation reject;不 attach、不恢复 claim | +| S-17 | any + maintenance authorization failure | same state | no mutation;audit reject reason,不产生 delivery | +| S-18 | any + same-tick timeout/reconnect/admin CAS loser | winner state | loser 重读 generation;不二次 handoff、不重复释放 claim | +| S-19 | Paused + resume with matching owner/generation | Running | claim 保留;继续原 checkpoint/escrow,不增 Q | +| S-20 | Paused + disconnect/shutdown/dimension/complete/cancel | by S-05..S-09 | 与 Running 使用同一 cause policy;不得因 Paused 绕过 handoff | +| S-21 | Suspended + duplicate disconnect/shutdown/pause | Suspended | 幂等 no-op;不刷新 TTL、不增 Q | +| S-22 | HandoffPreparing + duplicate gameplay/terminal event | HandoffPreparing | 重试同一 generation/payload;禁止生成第二 delivery_id | +| S-23 | nonterminal + event not admitted by state/cause matrix | same state | typed reject + audit;无 claim/checkpoint/quota effect | + +**唯一 teardown linearization point 是 S-14 的 durable handoff commit。** 从 S-14 起,worker 的 retry、lease expiry、malformed payload 或 inventory failure 只改变 obligation,不重新 attach gameplay session/claim。R1 不允许 direct terminal-delivery alternative;R10 的同步 `deliver` 只能是 obligation worker 内部的 transaction primitive。 + +### 3.1 Domain policy projection + +- `Checkpointed` 的 `Disconnect`/`Shutdown` 是可恢复暂停,不退款;`DimensionChange`、`InvalidRestore`、`SuspensionExpired`、管理员结案才进入 terminal refund/delivery。 +- `Volatile` 遇 disconnect/dimension/shutdown 立即进入 terminal handoff;不留下 owner、claim 或 settling。 +- `Completed` 只交付完整产物,不退 inputs;`VoluntaryCancel` 才执行域既有规则(craft 未完成部分 70% 返还)。 +- 所有 refund/release 通过 canonical obligation payload 和 R10 worker;qi 变化仍必须走 `qi_physics::ledger::QiTransfer`。 +- maintenance allow 必须分别覆盖 console positive 与 bound capability positive;wrong executor、offline username、伪造 owner 和普通玩家均拒绝。 +- retention 常量固定:`SESSION_DELIVERY_RESULT_REPLAY_TTL_TICKS = 12_096_000`(7 日)、`SESSION_DELIVERY_TOMBSTONE_TTL_TICKS = 51_840_000`(30 日)、`MAX_SESSION_DELIVERY_HISTORY_ROWS = 65_536`、`MAX_SESSION_DELIVERY_HISTORY_BYTES = 134_217_728`。R3 tick rebase 保留真实 age,不刷新 horizon;达到 row 或 byte 上限时 O-26 fail-before-deliver。 + +## 4. Canonical terminal-obligation reducer(R1/R3/R10 共用) + +`reduce_obligation(state, event) -> {next_state, quota_effect, worker_effect, retention_effect, audit_effect}` 是终态交付唯一规范。R3 提供 durable CAS/transaction;R10 提供唯一生产 worker;R1 只发出 handoff command 和消费结果。 + +| Row ID | obligation state + event | next state | `ΔQ` / effect | +|---|---|---|---| +| O-01 | Absent + new admission reservation commit | ReservedAwaitingClaim | `+Q`;quota update + unique reservation 同事务 | +| O-02 | Absent + quota full / unique conflict | Absent | `0`;fail closed | +| O-03 | ReservedAwaitingClaim + runtime busy claim win | ReservedLive | `0`;绑定 live session/generation,不重复计量 | +| O-04 | ReservedLive + matching suspended restore/retry | ReservedLive | `0`;复用 owner/bytes/generation,禁止 insert/+Q | +| O-05 | ReservedAwaitingClaim + busy race-loss/validation failure | CancelPending | `0`;runtime claim先释放,但 durable cleanup owner仍存在 | +| O-06 | CancelPending + cancel CAS commit | Absent | `-Q`;删除 reservation;重复/CAS loser `0` | +| O-07 | CancelPending + persistence failure | CancelPending | `0`;写入/保留 `next_retry`, attempts, generation;live reconciliation 必须重试 | +| O-08 | ReservedLive + terminal handoff transaction commit | Pending | `0`;reservation→outbox,不重复计量;payload bytes/digest 固定 | +| O-09 | ReservedLive + handoff transaction failure before commit | ReservedLive | `0`;checkpoint/claim/reservation 可重试,不能部分删除 | +| O-10 | Pending + worker claim CAS win | InFlight | `0`;写 lease/generation | +| O-11 | Pending + duplicate claim/CAS loser | Pending | `0`;重读 authoritative row | +| O-12 | InFlight + lease expiry | Pending 或 DeadLetter | `0`;按 retry budget/age 转换,不 attach gameplay | +| O-13 | InFlight + retryable inventory/spill failure | Pending | `0`;backoff/attempts 更新 | +| O-14 | InFlight + malformed payload/digest mismatch | DeadLetter | `0`;fail closed、保留完整 payload、告警;禁止调用 deliver | +| O-15 | InFlight + claimed payload decode/validation success | InFlight | `0`;`DeliveryRequest` 只能由 canonical payload decode 派生 | +| O-16 | InFlight + inventory/spill + receipt transaction commit | ReceiptRetained | `-Q`;inventory/spill、receipt、obligation delete、quota release 同事务 | +| O-17 | InFlight + receipt already exists for same digest | ReceiptRetained | `0`;幂等 replay,不二次 deliver 或 release | +| O-18 | DeadLetter + authorized operator retry | Pending | `0`;CAS loser 不改变 quota | +| O-19 | DeadLetter + authorized resolve with complete disposition | DispositionRetained | `-Q`;完整 disposition、obligation delete、quota release 同事务 | +| O-20 | DeadLetter + resolve missing payload/disposition | DeadLetter | `0`;fail closed,继续占 Q | +| O-21 | Receipt/Disposition retained + replay before horizon | same | `0`;返回既有 receipt/disposition | +| O-22 | Receipt/Disposition retained + compaction watermark reached | CompactedTombstone | `0`;保留 bounded digest/idempotency tombstone,不保留无限 payload | +| O-23 | CompactedTombstone + old replay | CompactedTombstone | `0`;按 tombstone reject/ack,不重新交付 | +| O-24 | CompactedTombstone + GC watermark/replay horizon confirmed | GarbageCollected | `0`;只允许在 producer/outbox watermark 证明旧 obligation 不可重现后删除 | +| O-25 | ReservedAwaitingClaim + cancel-mark persistence failure | ReservedAwaitingClaim | `0`;reservation 本身仍是 durable cleanup owner;live retry + startup stale-reservation scanner,禁止超时即 `-Q` | +| O-26 | InFlight + receipt/history quota unavailable before deliver | Pending 或 DeadLetter | `0`;在 inventory mutation 前 fail closed;保留 payload/Q 并 backoff/告警 | +| O-27 | any nonterminal + stale generation/invalid event/CAS loser | authoritative state | `0`;typed reject/audit,重读 row;不得隐式 insert、release、deliver | + +### 4.1 Obligation invariants + +1. 每个 `session_key` 恰有零或一个 durable obligation owner;`quota_used` 等于 `ReservedAwaitingClaim`、`ReservedLive`、`CancelPending`、`Pending`、`InFlight`、`DeadLetter` 的 Q 之和。payload envelope 固定含 `payload_schema_version`、`delivery_id`、`session_key`、terminal `generation`、recipient `PlayerKey`、cause、item/refund entries;O-08 首次写入的 exact bytes 是唯一权威身份,后续不得重序列化替换。 +2. `reserve_new` 只产生 O-01 的 `+Q`;claim win O-03、matching restore/retry O-04、handoff O-08、retry/lease O-12/O-13、所有 CAS loser 均 `0`。 +3. 只有 O-06 成功取消或 O-16/O-19 成功持久化终结结果才 `-Q`。取消标记或删除失败不能把 quota 留成无主 reservation:O-25 由原 reservation 继续担当 durable owner,O-07 由 `CancelPending` row 担当;scanner/retry 均不得凭 age 直接释放。 +4. `payload` 是 canonical serialized bytes;`payload_digest = SHA-256(payload)`。worker 的 `DeliveryRequest` 必须从 claimed payload decode,并在 O-16 transaction 中以 digest/semantic item set 对拍;不存在独立可替换的 caller payload。 +5. payload 序列化后恰为 `SESSION_DELIVERY_MAX_PAYLOAD_BYTES` 可接受;`+1` 在新增 escrow/output 被接受前 fail closed。payload 一旦进入 O-08 不可增长。 +6. `ReceiptRetained`/`DispositionRetained` 不是无限历史:完整结果按 §3.1 的 replay TTL 保留,达到 producer/outbox GC watermark 后转 O-22 bounded tombstone,再按 §3.1 的 tombstone TTL 满足 O-24 才 GC。R1 是 retention 数值与边界的唯一 contract owner;R3 只实现 table/index/compaction scheduling 与 O-26 deliver 前容量预留,不得另定 horizon、row/byte limit 或重启时刷新 age。 +7. O-08 成功后 gameplay claim 已由 S-14 释放;O-12/O-13/O-14/O-18 不得恢复 session。DeadLetter 无 runtime claim 但继续占 Q。 + +## 5. 接缝与 artifact ledger + +下表只说明 R1 提供/消费的 artifact;跨轨 owner/order 引用 master §4.1 和 PR 1902,不在 R1 复制 sequencing: + +| Artifact / row | R1 提供或消费 | canonical evidence | +|---|---|---| +| `InteractionSession` / S-01..S-23 | R1 producer;domain adapters consumer | session reducer trace | +| `SessionKey`/`PlayerKey`/generation | R1 producer;R3/R4/R6/R7/R10 consumer | stale generation S-16/S-23 | +| reservation/quota/outbox / O-01..O-27 | R3 durable producer;R1 semantic consumer;R10 worker consumer | quota invariant 1-3 | +| payload/digest/receipt/retention | R3 storage + R10 transaction;R1 consumes result | O-14..O-27 | +| stable `placed_id` | R3 P4 provider;R4 lookup;R1 checkpoint/restore consumer | S-10/S-11 | +| craft wire/schema | A-CS/R6/R4/R7 按 master owner;R1 只消费 hydrated identity/phase | PR 1902 contract-first/activation rows | +| coupled `TsyPresence` snapshot | R3 provider;R1 reconnect/gate consumer | full-old/full-new snapshot trace | + +## 6. Derived acceptance index + +验收只引用 row ID,不重新定义状态: + +1. `session_trace_matrix`:S-01..S-23 覆盖 admission、pause/resume、complete、cancel、disconnect、dimension、shutdown、restore、TTL、admin、duplicate、invalid、stale/CAS loser;每条 trace 逐步执行 `reduce_session`。 +2. `obligation_trace_matrix`:O-01..O-27 覆盖 quota-full、claim win、busy loser、cancel-mark/delete persistence failure 与 live/restart retry、restore reuse、handoff crash、lease expiry、retry/dead-letter、malformed/mismatched payload、history quota full、receipt replay、resolve fail-closed、retention/GC、invalid/stale event。 +3. `payload_identity`:claimed bytes、digest、derived request、inventory receipt 四者对拍;注入 payload A/request B 必须命中 O-14,不能调用 deliver 或 ack 任一 payload。 +4. `quota_conservation`:两个 SQLite connection 竞争最后 row/bytes;恰一个 O-01 成功,其余 O-02;race loser 的 cancel-mark 成功走 O-05→O-07/O-06,标记失败走 O-25 并由 live/startup reconciliation 收敛,不泄漏或双扣 Q。 +5. `handoff_crash_atomicity`:outbox insert 前、terminal checkpoint 前、commit 后 ack 前强杀;结果只能 O-09 可重试或 O-08/Pending 可重放,S-14 不 reopen。 +6. `delivery_history_bound`:receipt/disposition 在 horizon 前走 O-21 replay,达到 watermark 后为 O-22 tombstone,再由 O-24 GC;history quota 满命中 O-26,inventory 不 mutation,storage 不无限增长。 +7. `maintenance_auth`:console allow、current-executor capability allow、wrong-executor capability deny、offline/username/owner spoof deny。 +8. `workbench_restore`:`workbench_key` 0/1/u64::MAX 与 malformed/stale/despawned/cross-dimension/out-of-range;成功后只保存/恢复 `placed_id`,新 runtime Entity 可 rebind。 +9. `tsy_presence_snapshot`:routine autosave、disconnect、shutdown 每个写边界 crash 后 presence/position/dimension 只能全旧或全新。 +10. named bot scenarios:`session_disconnect_cleanup`、`session_dimension_transfer`、`session_restart_recovery`、`session_busy_mutex`、`session_full_inventory_delivery`、`session_suspension_reclamation`、`session_delivery_crash_atomicity`、`session_termination_cause_matrix`、`session_craft_pause_resume_wire`、`session_tsy_presence_relog`、`session_pending_insight_offer_deadline` 均只引用上述 row/trace ID。 + +## 7. 本轨实施阶段 + +### P1 — framework + craft adapter + +只落 `server/src/session/{mod.rs,registry.rs,lifecycle.rs}`、S reducer、registry/busy API 和 contract pins。framework 可在 master Wave 允许时先合入,但 craft production adapter 只有 master 的真实 artifact/cutover rows 全部满足后才启用;不以 mock、fixture 或未存在的 R6/R4/R7 consumer 宣称可达。 + +### P2 — alchemy / forge / lingtian + +checkpointed furnace/station 使用 S-07/S-14 与 O-08/O-16;delivery failure 只推进 O-13,不重新 attach。lingtian 六类 actor 使用 volatile S-08;不在线 tick。 + +### P3 — gathering / mineral / spiritwood / world interaction + +volatile adapter 统一释放 target claim;TSY Search/Extract 双向 busy;TsyPresence 仅在 R3 coupled snapshot guarded restore 通过后 attach。 + +### P4 — bot/e2e/归档 + +执行 §6 derived index,确认所有吸收项已有唯一 owner,再补 Finish Evidence;不修改其他轨文档的 authority 表述。 + +## 8. 开放问题 + +1. retention 数值与边界由本 canonical contract §3.1 唯一冻结;R3 P0 只选择 table/index/compaction scheduling 并实现这些常量,不得扩大 horizon/上限或在重启时刷新 age。 +2. R3 需选择 `CancelPending` 独立表或 reservation row 内嵌 retry metadata;两者必须实现相同 O-07/O-25 语义。 +3. 具体 domain escrow/refund 数值沿用各域既有 plan;不得在本 canonical protocol 新增经济规则。 + +## Finish Evidence + +> 迁入 `finished_plans/` 前填写各 S/O row 的实现路径、关键 commit、测试命令与数量、真实 producer→consumer→cutover evidence、receipt retention evidence,以及未完成的跨轨依赖。 diff --git a/docs/plans-skeleton/plan-agent-craft-schema-v1.md b/docs/plans-skeleton/plan-agent-craft-schema-v1.md new file mode 100644 index 000000000..1c4df8d23 --- /dev/null +++ b/docs/plans-skeleton/plan-agent-craft-schema-v1.md @@ -0,0 +1,70 @@ +# plan-agent-craft-schema-v1 — Craft lifecycle Agent schema 生产批次(总纲 A-CS) + +> 所属总纲:`plan-refactor-master-v1.md`。遵循 PR 1902 settled rulings:TypeBox 是 shape/validation source of truth;A-CS 拥有 craft domain contracts,R6 只拥有 generation/wire machinery 与 atomic activation。 + +## 阶段总览 + +| 阶段 | 交付物 | 状态 | +|---|---|---| +| P0 | 实际 source/union/registry/generated/dist inventory 与 row set | ⬜ | +| P1 | TypeBox domain contracts、union/registry/exports、contract tests | ⬜ | +| P2 | generated schema、dist、freshness gate | ⬜ | +| P3 | R6 handoff SHA/digest/derived counts | ⬜ | + +## 1. P0 实际 inventory(禁止虚构 baseline) + +2026-08-04 对当前 source 的核对结果:`craft.ts` 有 standalone `CraftStartReqV1` 与 `CraftSessionStateV1`,但 `ClientRequestV1`、`ServerDataV1`、`SCHEMA_REGISTRY` 对 `craft_start`、`craft_cancel`、`workbench_open`、craft session state 均无 membership。Rust/proto 的生产变体数不是 TypeBox baseline,不能写成 `113→116`。 + +| Row ID | production chain 所需 contract | 当前 TypeBox 状态 | A-CS 终态 | +|---|---|---|---| +| A-01 | `CraftOpen` | absent;取代 standalone/legacy `CraftStartReqV1` 的 lifecycle intent | required `target = Handcraft | Workbench { workbench_key }` | +| A-02 | `CraftPause` | absent | required `session_key + generation` | +| A-03 | `CraftResume` | absent | required `session_key + generation` | +| A-04 | `CraftCancel` | server/proto 已 live,TypeBox envelope absent | 纳入 authoritative C2S union;不由 Pause 替代 | +| A-05 | `WorkbenchOpen` | server/proto 已 live,TypeBox S2C union absent | 纳入 authoritative S2C union;产生 request-local `workbench_key` | +| A-06 | `CraftSessionStateV2` | 只有 standalone V1,S2C union absent | V2 纳入 union;删除 V1 production export/membership | + +P0 必须枚举每个 row 的 source→export→`ClientRequestV1`/`ServerDataV1`→`SCHEMA_REGISTRY`→generated→dist/runtime import 状态,并计算**当时真实** C2S/S2C type set。P1/P2 完成后的目标 count 是该集合实际去重后的派生值;主线 drift 先由其 owner 修复或显式登记,不把 Rust count 冒充 TypeBox count,也不为凑常量越界修无关 contract。 + +## 2. 冻结 shape + +- A-01 `CraftOpen.target` required:`Handcraft | Workbench { workbench_key }`。key 是 unsigned `u64` decimal string;缺失、负数、小数、科学计数法、空白、`>u64::MAX` 均拒绝;它不是 durable identity 或 capability。 +- A-02/A-03 只含 required `session_key + generation`,不得夹带 target 或替代 Cancel。 +- A-04 保留显式取消语义与既有 production discriminant;字段 inventory 必须与 proto/Rust live contract 对拍后冻结。 +- A-05 `WorkbenchOpen.entity_id` 是 A-01 `workbench_key` 的 producer;TypeBox/JSON 使用 decimal string,并锁 `0/1/u64::MAX`。 +- A-06 的五个 phase 都 required `session_key + generation`,且禁止 delivery obligation 字段。`Paused` 是唯一 Resume-eligible phase;`Running` 表示已活动且重复 Resume 不得重启;`Suspended` 必须等待 guarded restore 后由新 `Paused` projection 开放 Resume;`HandoffPreparing` 与 `Ended` 均 terminal/non-resumable。 + +## 3. 阶段交付物 + +### P1 — TypeBox domain content + +在 `agent/packages/schema/src/{craft.ts,client-request.ts,server-data.ts,schema-registry.ts,index.ts}` 落 A-01..A-06,注册/export 到相应 envelope 与 registry。正反样本覆盖 target/key、Pause/Resume identity、Cancel、WorkbenchOpen producer,并按下表逐行 pin StateV2: + +| phase | identity rule | client intent rule | +|---|---|---| +| `Running` | required matching `session_key + generation` | non-resumable;重复 Resume typed reject/no restart | +| `Paused` | required matching `session_key + generation` | 唯一 Resume-eligible;只允许同 identity/generation 恰一次 Resume | +| `Suspended` | required matching `session_key + generation` | non-resumable;等待 guarded restore 后新的 `Paused` projection | +| `HandoffPreparing` | required matching `session_key + generation` | terminalizing;Resume/Pause/Cancel 均不得重新打开 session | +| `Ended` | required matching `session_key + generation` | terminal;所有 gameplay intent stale reject | + +每个 phase 各有 valid sample,并分别覆盖缺 `session_key`、缺 `generation`、stale generation、mismatched identity;含 obligation phase/字段或未知 phase 必须拒绝。P1 只可声明 contract-first,不宣称 producer→consumer production 可达。 + +### P2 — generated / dist + +更新 `GENERATED_SCHEMA_FILES`、六个单项 schema、envelope schemas 与 committed dist。source/registry/generated/dist 同一提交;删除或篡改任一层时 freshness test 失败,clean checkout 可 runtime import A-01..A-06。 + +### P3 — R6 handoff + +记录 merge SHA、schema digest、A-row source/generated/dist/runtime-import 对拍,以及从最终 registry **程序化计算**的 C2S/S2C counts/type sets。R6 只接受该冻结版本,并按 PR 1902 负责 proto/Rust/client machinery;production activation 由 master cutover row 管理,不是 A-CS P3 的下游实现验收。 + +## 4. 验收与边界 + +- 必跑 `cd agent/packages/schema && npm test` 与 `cd agent && npm run build -w @bong/schema`。 +- acceptance 逐 A-01..A-06 证明正反 sample、union membership、registry membership、freshness、generated/dist/runtime import;A-06 必须逐 `Running/Paused/Suspended/HandoffPreparing/Ended` 执行 §3 phase/identity/client-intent 矩阵的全部正反 case,不能用通用样本代替;count 断言从 registry 派生,不出现手写 113/116。 +- 不改 proto、Rust、Java、gameplay handler/session;不吸收全量 schema drift plan。若无关 drift 阻断 envelope freshness,记录真实 owner/prerequisite,不擅自扩 scope。 +- 跨轨 owner/order/cutover 仅引用 master §3/§4.1 与 PR 1902,不在本 plan 复制依赖箭头。 + +## Finish Evidence + +> 迁入 `finished_plans/` 前填写 A-01..A-06 的落地路径、commit SHA/日期、测试结果、registry-derived counts/type sets、source/generated/dist/runtime-import 对拍及遗留 drift owner。 diff --git a/docs/plans-skeleton/plan-refactor-c2s-gate-v1.md b/docs/plans-skeleton/plan-refactor-c2s-gate-v1.md index bb02a4f60..a5b1467de 100644 --- a/docs/plans-skeleton/plan-refactor-c2s-gate-v1.md +++ b/docs/plans-skeleton/plan-refactor-c2s-gate-v1.md @@ -1,10 +1,10 @@ # plan-refactor-c2s-gate-v1 — C2S 请求统一门禁中间件 + client_request_handler 巨石拆分(重构轨 R4) -> 所属总纲:`plan-refactor-master-v1.md`。一句话:给 113 种 C2S 请求建统一的声明式门禁层(距离/维度/所有权/状态前置),同时把 20082 行的 `client_request_handler.rs`(单函数 2438 行、17 个 SystemParam)拆成按域注册的 handler 模块——"只信裸坐标可跨维远程操作"这一整簇(20+ 份 plan)从此在架构上不可能。 +> 所属总纲:`plan-refactor-master-v1.md`。一句话:给 authoritative registry 派生的全部 C2S 请求建立声明式门禁层(距离/维度/所有权/状态前置),并把 20082 行的 `client_request_handler.rs` 拆成按域注册的 handler 模块。 ## 现状证据(2026-07-27 侦察) -- `network/client_request_handler.rs:522` `handle_client_request_payloads` 单函数跨 522-2960 行,巨型 match 覆盖 `ClientRequestV1` 的 113 个变体;`CombatRequestParams` 一个 SystemParam 30 个字段。 +- `network/client_request_handler.rs:522` `handle_client_request_payloads` 单函数跨 522-2960 行;当前 production Rust match 与 TypeBox union 已知存在 drift,因此 P0 必须分别 inventory 并在 A-CS/R6 完成后以 authoritative registry 派生目标集合,禁止手写 113/116。`CombatRequestParams` 一个 SystemParam 30 个字段。 - 门禁分散手写:8 个文件各自定义 `*_MAX_DISTANCE`/`*_RANGE_SQ`(`client_request_handler.rs:468-469`、`craft/workbench.rs:76` Chebyshev、`mineral/probe.rs`、`npc/relic.rs`、`supply_coffin/authority.rs`、`zhenfa/network_array.rs` 等),距离度量都不统一;`CurrentDimension` 在 handler 内被 13 处内联比对,无统一 helper。 - 后果即 bughunt 大簇:跨维开工作台/布阵/夺舍/交易/拾取、无 reach 校验放方块、先扣物品后校验吞丹、无所有权校验拆棺。 - `zone-lookup-overworld-hardcode`:zone 查找硬编码主世界,是维度感知缺失的底层同源。 @@ -14,14 +14,14 @@ - **进料**:`bong:client_request` 单通道(既有,C2S 本就单轨)、玩家实体(Position/CurrentDimension/状态组件)、R1 session 忙态、R10 inventory 事务。 - **出料**:校验通过的请求进各域 handler;拒绝走统一 reject 回执(带原因码,client 侧 toast/HUD 可消费——对齐 unconsumed-event-feedback 的方向但只做 gate 拒绝部分)。 - **共享类型**:新 `server/src/network/gate/`——`GateSpec { max_distance(度量统一), same_dimension, ownership, state_preconditions }` 按请求类型声明;维度感知 zone 查找 helper。 -- **跨仓库契约**:wire 形状不变(113 变体不动);reject 回执若新增字段走 R6 的契约流程。 +- **跨仓库契约**:wire 形状通常不变(现行 enum 全量);reject 回执若新增字段走 R6 的契约流程。R1 craft pause/resume 例外消费 R6 新增的 `CraftOpen`/`CraftPause`/`CraftResume`,本轨负责 production decode/dispatch,并以 owner、phase 与 busy `GateSpec` 拒绝伪造 resume/重复包。Workbench 初次 `CraftOpen` 的 `workbench_key` 只解析当前进程 runtime entity;R4 重验实体/维度/距离/facility 后还必须从 R3 P4 registry取得 stable `placed_id` 并把它交给 R1 建 claim,mapping 缺失/重复/未 hydrate 一律拒绝,禁止把 `Entity::to_bits()` 持久化。 ## 阶段 -- ⬜ P0 设计收口 + 吸收清单验真:113 个变体普查(每个标注应有的门禁四元组现状);冻结 `GateSpec` 与拒绝回执语义;等 #1287(冷却重构,同文件大改)merge 定基线。 -- ⬜ P1 门禁中间件落地:gate 层上线,先给"已知漏洞簇"的 ~20 个请求类型挂 spec(吸收清单全命中),旧内联校验保留并行断言一个版本期。 -- ⬜ P2 巨石拆分批次 A:巨型 match 拆为按域 handler 注册表(combat/production/world/social/npc 五组),行为不变,bot 场景锁住;inventory pickup handler 必须从 ECS `CurrentDimension`、authoritative position/observation range 与 owner/private permission 构造 R10 `PickupAuthorization`,禁止仅凭 client XYZ/instance id 调用 txn,且把 R10 accepted/rejected outcome 交给 R6 emit API。**本批次的 inventory pickup consumer 仅在 R10 P3 pickup/merge txn、R5 P3 attrition API 与 R6 P4 receipt API 均已合入后实施;此前不得以 mock 或旧 R6 P1 schema 接线。** -- ⬜ P3 巨石拆分批次 B + 全量挂 spec + 删旧:113 变体全部声明门禁(含显式 `no_gate` 声明,杜绝静默无门禁);删除各域内联距离常量与重复维度判断。 +- ⬜ P0 设计收口 + 吸收清单验真:分别普查 production Rust match 与 TypeBox authoritative registry,记录 drift owner;每个 registry-derived C2S variant 标注门禁四元组,冻结 `GateSpec` 与拒绝回执。 +- ⬜ P1 门禁中间件落地:gate 层上线,先给已知漏洞簇挂 spec;craft production decode/dispatch 只在 master M-07/M-10 activation 时接入 A-CS A-01..A-04,runtime key negatives 属本阶段 owner evidence。contract-first handler declarations 可先合入,但不以未激活 stub 宣称 live。 +- ⬜ P2 巨石拆分批次 A:巨型 match 拆为按域 handler 注册表(combat/production/world/social/npc 五组),行为不变,bot 场景锁住;pickup handler 的 authorization/txn/receipt 接线只在 master M-14/M-15 与 R5 ledger artifact 可用后实施,不得以 mock 或旧 schema 接线。 +- ⬜ P3 巨石拆分批次 B + 全量挂 spec + 删旧:authoritative registry-derived C2S type set 全部声明门禁(含显式 `no_gate`);删除重复距离/维度判断。 - ⬜ P4 bot 验收 + 吸收 plan 批量归档。 ## 吸收清单(短名省略 plan-bughunt- 前缀与 -v1 后缀) @@ -33,7 +33,7 @@ skeleton:alchemy-furnace-scope-gate、block-place-reach-gate、coffin-reclaim- - 独占:`network/client_request_handler.rs`(拆解)、新 `network/gate/`、各域内联距离/维度校验行的删除。 - 不碰:`*_emit.rs` S2C 侧(R6)、session 内部(R1)、inventory 事务(R10)。 -- 依赖:基线等 #1287 merge;建议在 R6 的 emit 侧稳定后开 P2(同在 network/ 目录,文件不相交但相邻);P0/P1 可先行。 +- 依赖/order/cutover 只引用 master M-02/M-05/M-07/M-10 与 PR 1902;本 plan 不建立第二套 sequencing。R4 P1 的 runtime rejection pins 在 R4 自有阶段完成,不作为 R6 P1 验收。 ## bot 验收场景 @@ -41,7 +41,7 @@ skeleton:alchemy-furnace-scope-gate、block-place-reach-gate、coffin-reclaim- 2. `gate_reach`:超距放方块/开炉/采灵田→拒绝;贴脸→放行。 3. `gate_ownership`:拆他人棺/取他人容器→拒绝。 4. `gate_state_precondition`:给丹先校验后扣(满包/死亡目标不吞丹);丹毒超阈值禁服。 -5. `gate_matrix_sweep`:对 113 变体做参数化扫描(合法/超距/跨维三档),断言与声明的 GateSpec 一致——这是本轨的主回归门。 +5. `gate_matrix_sweep`:从 authoritative registry 派生 type set,逐 variant 执行声明的合法/超距/跨维/no_gate trace;craft 另覆盖 owner mismatch、wrong phase、conflicting busy claim、duplicate/replay、stale identity、runtime-key malformed/stale/despawned/cross-dimension/out-of-range 与合法 S-02 admission。 ## 开放问题(pre-P0 收口) diff --git a/docs/plans-skeleton/plan-refactor-client-ui-base-v1.md b/docs/plans-skeleton/plan-refactor-client-ui-base-v1.md index 2084fc86f..eb7940dfe 100644 --- a/docs/plans-skeleton/plan-refactor-client-ui-base-v1.md +++ b/docs/plans-skeleton/plan-refactor-client-ui-base-v1.md @@ -16,13 +16,13 @@ - **进料**:R2 的 `SessionScopedStore`(Screen 订阅的一律是会话态 store);`ServerDataRouter` handler(一律经 client-thread marshal 投递 UI)。 - **出料**:Screen/HUD 展示;HUD 纪律沿用既有 memory 约束(未解锁隐藏不灰掉、沉浸式极简)。 - **共享类型**:新 `BongScreenBase`(生命周期 + 订阅 + 关闭清理)、`DiffListWidget`(推广 craft 范本)、`BongKeybindRegistry`(注册时冲突检测 + 测试期断言)、`ClientThreadMarshal` helper、`ScreenOpenPolicy`(礼貌抢屏:战斗中/已有模态时排队)。 -- **跨仓库契约**:零 wire 改动。 +- **跨仓库契约**:本轨不定义 wire,只消费 A-CS A-row、master M-02/M-07/M-09/M-10 与 R1 S-row。Idle/no-session 初次 `CraftOpen` target 为 `Handcraft` 或 retained `Workbench { workbench_key }`;close 发 `CraftPause`,explicit cancel 发 `CraftCancel`,仅匹配 server-hydrated `Paused` session 发一次 `CraftResume`。R1 `HandoffPreparing`/`Ended` 或 stale identity 均不发 Resume;delivery Pending/InFlight/DeadLetter 是 obligation 状态,R7 不把它们存为 resumable gameplay phase。 ## 阶段 - ⬜ P0 设计收口 + 吸收清单验真:92 处 fill(100) 全量分类(根节点合法/子节点顶飞);28 Screen 普查;冻结基类 API 与四个共享组件。 - ⬜ P1 基础组件落地:BongScreenBase/DiffListWidget/KeybindRegistry/ClientThreadMarshal/ScreenOpenPolicy 上线;keybind 冲突全数改绑(T/L/O/U/G 簇)。 -- ⬜ P2 Screen 迁移批次 A:炼丹/锻造/手搓/交易屏迁基类,fill(100) 顶飞点与 clearChildren 回弹点随迁修复。 +- ⬜ P2 Screen 迁移批次 A:炼丹/锻造/手搓/交易屏迁基类,随迁修复 fill(100)/clearChildren;Craft Screen 接线为 Idle→带 `Handcraft` 或 retained `Workbench { workbench_key }` 的 `CraftOpen`、close→`CraftPause`、显式取消→`CraftCancel`、匹配 paused hydrate→恰一次 `CraftResume`,并以 client 单测锁住 target key roundtrip、四种 intent 不互相替代及不可恢复 phase/stale identity 不发 Resume。 - ⬜ P3 InspectScreen 拆解:按 tab/section 拆组件文件(body/container/tooltip 已有雏形),行为不变。 - ⬜ P4 Screen 迁移批次 B + 网络线程 marshal 强制(handler 层静态检查/测试)+ 删旧。 - ⬜ P5 验收 + 吸收 plan 批量归档。 @@ -36,13 +36,14 @@ skeleton:alchemy-screen-fill100-eviction 与 alchemy-screen-fill-overflow( - 独占:client 全部 Screen/`ui/`/`hud/` 结构性改动、keybind 注册、`InspectScreen.java`。 - 不碰:store 生命周期接口(R2 域,本轨消费);`network/` 桥与 router(R6 域——marshal helper 由本轨提供、在 handler 注册处的接线与 R6 协调);server 一切。 -- 依赖:R2 P1 先合(基类要绑 SessionScopedStore);与 R6 在 handler 投递点有一条接缝,P4 前对齐。 +- 依赖:本轨只引用 master M-02/M-07/M-08/M-09/M-10;R2 Store、R6 machinery、R4 gate 与 R1 session 的 production 接缝在 master atomic activation row 完成前只能提交 contract pins,不宣称端到端可达。 ## 验收 -bot 测不到 client 渲染,本轨主验收 = client 单测(基类生命周期 pin、DiffListWidget 滚动保持、keybind 注册表无冲突断言、marshal 强制扫描)+ `./gradlew runClient` 人工过一遍五大屏。bot 配合:`ui_c2s_smoke`(各屏的 C2S 动作链路照常可达,防拆解断线)。 +bot 测不到 client 渲染,本轨主验收 = client 单测(基类生命周期 pin、DiffListWidget 滚动保持、keybind 注册表无冲突断言、marshal 强制扫描、CraftOpen/CraftPause/CraftCancel/CraftResume 四条 intent producer pin)+ `./gradlew runClient` 人工过一遍五大屏。bot 配合:`ui_c2s_smoke`(各屏的 C2S 动作链路照常可达,防拆解断线)。 ## 开放问题(pre-P0 收口) 1. InspectScreen 拆解粒度(按 tab 还是按 section);拆解与 R10 server 侧 inventory 拆分是否同窗口进行。 2. ScreenOpenPolicy 的排队语义(战斗中挂起邀请到何时弹出)——涉及玩法体验,需人工拍板。 +3. `Running`/`Suspended` hydration 的 Resume producer negative test 枚举本轮不扩展;延期到总纲 `plan-refactor-master-v1.md §3 Wave 2` 的 R7 producer 验收窗口,理由是需与完整 phase enum 及 R6 store generation-CAS 一次性对拍。 diff --git a/docs/plans-skeleton/plan-refactor-master-v1.md b/docs/plans-skeleton/plan-refactor-master-v1.md index 28e25c1ce..c73603e31 100644 --- a/docs/plans-skeleton/plan-refactor-master-v1.md +++ b/docs/plans-skeleton/plan-refactor-master-v1.md @@ -6,7 +6,7 @@ ## 0. 范围与铁律 -- **只重构 `server/` + `client/`**。`agent/`、`worldgen/`、`library-web/` 不动,相关 plan 独立保留(§6.11-6.12)。 +- **重构实施只改 `server/` + `client/`**。`agent/`、`worldgen/`、`library-web/` 不由 R1-R10 修改,相关 plan 独立保留(§6.11-6.12);R 轨需要 agent-side schema 变更时,必须由 §6.11 Agent 轨作为独立 production owner 先交付,R 轨只消费其冻结产物。 - 对外契约(Redis IPC、proto schema)原则上不动形状;确需变更走 buf breaking + samples 同步,agent 侧只做被动 regenerate。**不写兼容层**——切换一次到位,删旧路径。 - 真元守恒律、worldview 正典、招式 A/V 差异化红线全部继续生效。 - **测试方针(用户 2026-07-27 指示,仅限重构轨道,覆盖根 CLAUDE.md「饱和化测试」节)**: @@ -48,19 +48,44 @@ | V | `plan-bot-e2e-coverage-v1`(既有 skeleton 直接促升,不另立) | bot 场景 P1-P6 扩容 + CI 假绿修复 + build token 脚本 | `scripts/bot/**`、CI | ~9 | | 基建 | `plan-registry-datafication-v1`(既有 skeleton 直接促升) | 硬编码配方/功法/方块表迁数据 + fail-fast | 三张表 | 自身 | -## 3. 波次与依赖 - -- **Wave 0(立即并行)**:V(bot 骨干 + build token 最先)、R3、R5、R2、registry-datafication;同时全部轨道的 P0(设计收口 + 吸收清单验真)都可开工。 -- **Wave 1**:R6(R2 合入后)、R7(R2 合入后)、R1(R3 P1 合入后)。 -- **Wave 2**:R4(#1287 + R6 P1 后)、R9(R5/R6/R2 P1 后)、R10(R3 P1 后)。 -- 近完成独立 plan(§6.9)在 Wave 0 窗口内优先收尾清场。 -- R5 P1(字段收私有的全仓编译大爆破)挑在飞 PR 队列清空的窗口单独合入。 - -## 4. 文件所有权矩阵(防并行打架,冲突时以本表为准) - -- `persistence/**`+autosave=R3;`session/`+7 域 session.rs=R1;`client_request_handler.rs`+`gate/`=R4;`*_emit.rs` 公共层+`proto_convert.rs`=R6;`qi_physics/**`+qi 字段直写行=R5;`inventory/**`=R10;cast/AV emit+skill 注册=R9。 -- client:Store 生命周期+`clearClientStateOnDisconnect` 区段=R2;channel 注册区段+桥+router=R6(与 R2 同文件不同区段,merge 前互 fetch);Screen/hud/keybind/InspectScreen=R7;combat cast store=R9。 -- 任何轨道碰他轨文件:只允许"消费对方冻结后的 API",不允许改对方独占文件;接缝 API 归被依赖方定义。 +## 3. 唯一跨轨波次与裁决 + +本节与 §4.1 是计划族唯一 start/order/cutover authority。各 track plan 只能引用 row ID,不能复制或反写依赖图。PR 1902(`origin/docs/master-r9-r6-ownership-adjudication`)五项裁决作为 settled context:TypeBox source of truth、domain content/generation machinery 分权、contract-first 不等于 live、production activation 原子切换、master Wave 表唯一 authority。 + +- **Wave 0 — source/provider foundations**:V、R2、R3、R5、registry-datafication 与各轨 P0;Agent A-CS 执行 M-01。 +- **Wave 1 — contract-first infrastructure**:R6 M-02(A-CS 后)、R1 M-03 framework-only(R3 storage seam 后)、R7 UI 基础设施(R2 后)。这些 row 可交付 declarations/converters/reducers/tests/stubs,但不得宣称 craft live。 +- **Wave 2 — durable providers and atomic activation**:R3 M-04/M-05/M-12、R10 M-06/M-13/M-15、R4 M-07/M-15、R7 M-08、R1 M-09/M-10、R6 M-14/M-15。M-10 是唯一 craft production cutover:real state producer、wire emit/bridge、C2S gate/handler、client producer/store 与旧 receiver removal 必须在安全 merge unit 内一起启用;dropped-loot projection/pickup 则分别由 M-14/M-15 原子切换,禁止长期 dual emit 或以 fixture 冒充 live。 +- R1 其余 checkpointed adapters 在 M-04/M-06 后迁移;volatile adapters 仍须使用 canonical S/O traces。TSY attach 另等 R3 coupled snapshot guarded restore。 + +## 4. 文件所有权矩阵 + +- `persistence/**`+autosave=R3;`session/`+七域 adapter=R1;`client_request_handler.rs`+`gate/`=R4;emit/converter/client network machinery=R6;`qi_physics/**`=R5;`inventory/**`=R10;cast domain=R9。 +- client Store lifecycle=R2;Screen/hud/keybind=R7;combat cast store=R9。 +- `agent/packages/schema/**` craft domain content/generated/dist=A-CS;R6 不修改 Agent artifacts,只消费冻结 SHA。 +- `workbench_key` 是 request-local runtime locator;R4 校验当前 Entity 后必须映射 R3 stable `placed_id`,R1 checkpoint 仅保存 `placed_id`。missing/duplicate/unhydrated mapping fail closed。 +- 任一轨碰他轨 artifact 只消费冻结 API;API owner、production consumer 和 cutover evidence 均以 §4.1 为准。 + +### 4.1 Artifact / cutover ledger + +| Row ID | Artifact / cutover | Producer | Production consumer / completion evidence | +|---|---|---|---| +| M-01 | craft TypeBox A-01..A-06 source/generated/dist | A-CS P1-P3 | R6 M-02;registry-derived type sets、freshness、runtime import | +| M-02 | craft proto/Rust/client wire machinery、emit API/stub | R6 P1 | M-10 activation;只验 converter/roundtrip,不要求 downstream live | +| M-03 | `InteractionSession`、S reducer、registry/busy API | R1 P1 framework | R1 adapters;S-01..S-23 traces | +| M-04 | reservation/quota/outbox/CAS/history storage | R3 P1 | R1 handoff + R10 worker;O-01..O-27 durable traces | +| M-05 | stable `placed_id` hydrate registry | R3 P4 placeable batch | R4 runtime→stable lookup;R1 S-10 restore | +| M-06 | `InventoryTxn::deliver` + `SessionDeliveryWorker` | R10 P1/P2c | O-10..O-21/O-26/O-27;payload digest binding、receipt atomicity | +| M-07 | craft C2S production gate/handler | R4 P1 | R1 admission;malformed/stale/despawned/cross-dimension/out-of-range rejection | +| M-08 | Craft Screen Open/Pause/Cancel/Resume producers | R7 P2 | R4 handler;intent separation、Resume once | +| M-09 | `CraftStore` lifecycle + wire handler contract | R2 P1 + R6 M-02 | R7 P2;disconnect clear/stale generation reject | +| M-10 | craft production atomic activation | R1/R4/R6/R7 integration owner | prerequisites M-01..M-09;real response→screen→request→session→emit/store trace;删除 V1/旧 receiver | +| M-11 | checkpointed domain migration | R1 P1/P2 adapters | M-04 + M-06;S-07/S-14 与 O-08/O-16 crash trace | +| M-12 | coupled TSY presence/position/dimension snapshot | R3 P1 | R1 P3 attach;routine/disconnect/shutdown 全旧或全新 | +| M-13 | dropped-loot metadata、capacity 与纯 migration provider | R10 P1/P2 | R3 hydration;Public/OwnerOnly admission、spill/delete atomic seam | +| M-14 | dropped-loot guarded hydration 与 recipient-specific projection/page | R3 P2 + R6 P1/P2 | 同 revision 分片替换、visibility/dimension/range/owner filter;超限 fail closed | +| M-15 | pickup authorization、merge/placement、qi attrition 与 receipt atomic activation | R10 P3 + R5 ledger | R4 handler + R6 receipt;authorize→txn→attrition→drop delete 单事务 | + +闭环规则:类型引用、mock、fixture 或单独 stub 不算 production consumer。上游 artifact 可 contract-first 合入;只有表中 consumer/cutover evidence 存在后才可宣称 live。任何新增跨轨 artifact 先登记本表;track plan 只引用 M-row 与 canonical S/O/A-row。 ## 5. 工作流(GPT tmux 多会话) @@ -80,6 +105,7 @@ - **6.1-6.9 已入轨**:见 R1-R10 各文件吸收清单(合计 ~130 份)。 - **6.10 V 轨(bot 骨干 + 测试诚实性)**:bot-e2e-coverage(促升本体)、bot-combat-server-data-type-false-positive、bot-multibot-chat-visibility、bot-multibot-entity-spawn-visibility、e2e-command-anchor-rejected、task13-mutation-qi-zero-green、proto-breaking-check-shallow-skip(深检部分,与 R6 P4 联动);已知 server 侧缺口「fallback 平台 centered on origin 非 spawn」一并修。 - **6.11 Agent 轨(本次不重构,独立保留逐个消费)**:active——anticheat-tiandao-drop、niche-guardian-redis-dispatch、npc-combat-relic-schema-drift、pseudo-vein-agent-deadwire、war-participate-agent-command-drift、tiandao-schema-dist-start、server-data-s2c-schema-union-drift 的 TS 侧;skeleton——agent-ui-tiandao-revelation-vfx-flag-loss、alchemy-start-intervention-agent-drop、anqi-carrier-charged-agent-narration、arbiter-cjk-redaction-bypass、heart-demon-late-pregen-fallback、narration-target-prefix-routing、poi-novice-tiandao-narration-drain、technique-feedback-bridge、tiandao-agent-ui-click-context-loss、tsy-agent-ui-wrong-player-routing、tsy-enter-exit-agent-silent-drop、worldmodel-rollback-stub、rebirth-tiandao-bridge-gap、tsy-discovery-ui-target-fallback、player-chat-list-unbounded。 +- **6.11 A-CS production registration**:`docs/plans-skeleton/plan-agent-craft-schema-v1.md` 在 Wave 0 唯一生产四个 craft TypeBox/generated/dist artifacts,记录 SHA/count 供 R6;未归档时 R6/R4/R7/R1 craft gate 阻塞。 - **6.12 Worldgen 轨(独立保留)**:active——anomaly-raster-runtime-consumer、baolongwang-poi-consumer-gap、raster-check-required-layers、spirit-eye-raster-candidate-disconnect、structure-manifest-loot-consumer、tribulation-scorch-mineral-node-gap、worldgen-pipeline-root-cwd、worldgen-raster-check-cli-noop;skeleton——animal-air-spawn-gravity、spawn-safe-y-surface-drift、spawn-tutorial-poi-y-drift、sword-sea-zone-overlap、tsy-start-raster-env-gap、tsy-y-strata-overlay、worldgen-uint8-maximum-blend、zone-ecology-global-refuge、qi-density-same-source。 - **6.13 接线拍板轨(module-wiring-gaps-v2 为决策菜单,人工拍板后逐个拆实施 plan;重构后接线成本大降)**:module-wiring-gaps-v2、forge-lingtian-processing-deadpath、poi-trespass-refusal-runtime-gap、silent-signal-runtime-bridge、social-runtime-bridge-gap、k2-identity-social-renown-bridge、war-emergent-group-reputation-gap、npc-combat-gear-v2、social-anonymity-live-refresh-gap、unconsumed-event-feedback、zhenfa-array-flag-e2e-wiring、woliu-dying-master-runtime-gap。 - **6.14 Feature 轨(独立,注意 §5.6 冻结窗口)**:active——beast-horde、client-login-ux、container-filter-and-completion、gameplay-journey、gathering-tool-bind、halfstep-buff-calibration、iris-integration、nested-pack(已 WITHDRAWN)、social-v2、sou-da-che、satiety-hydration(在飞)、ci-redis-pull-resilience(#1291 返工中);skeleton——ancient-relic-payoff、bonecoin-wallet-bridge、craft-chain-items、dandao-mutation-gameplay、dazuo、first-technique-grant、lootcrate、neardeath-ux、newbie-30min-hooks-audit、block-break-integration(#1253,基建 skeleton,建议 Wave 2 后评估与 R4 关系)。 @@ -98,7 +124,7 @@ 1. 9 条轨道全部归档(各自 bot 场景常绿 + 吸收 plan 全部归档/验伪结案); 2. 三个 2 万行级 god file(inventory/mod.rs、client_request_handler.rs、persistence/mod.rs)不复存在,最大单文件 < 3000 行; -3. `qi_current` 裸写编译不过;client 无未登记的会话态 store;113 C2S 变体全部有显式 GateSpec/no_gate 声明;28 旁路 channel 收编或豁免登记; +3. `qi_current` 裸写编译不过;client 无未登记的会话态 store;authoritative registry 派生的全部 C2S 变体均有显式 GateSpec/no_gate 声明;28 旁路 channel 收编或豁免登记; 4. bot 场景数从 ~30 增至 ≥80,CI e2e 是唯一主门禁且无已知假绿。 5. `flash-review` label 下 open issue 全部显式处置(fixed / dup / 验伪关闭 / 促升 skeleton,见 §10),无静默积压。 diff --git a/docs/plans-skeleton/plan-refactor-persistence-slices-v1.md b/docs/plans-skeleton/plan-refactor-persistence-slices-v1.md index c582a0f86..8f415ad85 100644 --- a/docs/plans-skeleton/plan-refactor-persistence-slices-v1.md +++ b/docs/plans-skeleton/plan-refactor-persistence-slices-v1.md @@ -13,7 +13,7 @@ ## 接入面 - **进料**:SQLite(bong.db,沿用)、`shutdown.rs`(#1261 之后的关服链路)、`CultivationClock`(相对 tick 基准)。 -- **出料**:统一 Slice API 供各域注册:`load(guarded) / autosave(cadence) / flush_on_shutdown / tick_rebase`;R1 的 session 持久化钩子、各域运行态表全部走它。 +- **出料**:统一 Slice API:`load(guarded) / autosave(cadence) / flush_on_shutdown / tick_rebase`。R1 session persistence 只消费该接口;tick rebase 保留 suspension/retry/lease 的真实剩余时长与已消耗 age,不刷新租约。R3 durable 实现严格投影 R1 O-01..O-27:`reserve_new_terminal_obligation`(O-01/O-02)、`reuse_terminal_obligation`(O-04)、durable `CancelPending` reconciliation(O-05..O-07)、reservation→outbox atomic handoff(O-08/O-09)、claim/retry/dead-letter CAS(O-10..O-20)、receipt/disposition retention、bounded tombstone 与 watermark GC(O-21..O-24)。`TsyPresence` 与 player position/dimension 仍为独立 coupled snapshot contract;routine autosave、disconnect、shutdown 必须共用 transaction/version,crash 后只能全旧或全新。 - **共享类型**:新 `server/src/persistence/` 多文件模块(按域拆表定义 + 迁移链保持线性单入口);`PlayerSliceRegistry`(对齐 #1290 skeleton 的方向,直接吸收它)。 - **跨仓库契约**:零 wire 改动。 - **qi_physics 锚点**:任何带 qi 的快照持久化/恢复不得造成账面变化;恢复失败的兜底路径必须走 `release_dormant_qi_to_zone` 而非丢弃(对齐守恒律红旗清单)。 @@ -21,13 +21,12 @@ ## 阶段 - ⬜ P0 设计收口 + 吸收清单验真:53 张表普查归域;冻结 Slice trait(载入守护语义:读失败 = 保留旧行 + 告警 + 只读降级,绝不写回空态;flush registry;tick rebase 协议);等 #1288/#1289/#1261/#1259 merge 后定基线。 -- ⬜ P1 框架落地 + 巨石拆分:`persistence/` 按域拆文件(迁移链不变、行为不变);Slice 框架上线,KnownTechniques/Lifecycle(在飞 PR 的成果)平移为首批宿主;冻结 inventory slice hydration seam 及 `MigrationOutcome` consumer 边界,R3 不复制 inventory 网格规则,待 R10 P1 提供纯幂等迁移函数后接入。R3 P1 只能冻结该 seam,不得引用尚未合入的 R10 常量或实现。 -- ⬜ P2 载入守护推广:全部玩家 slice(SkillSet/Wounds/状态 buff/身份键……)收编,#1290 模式全量落地;dropped-loot slice 的有界 hydration guard 依赖 R10 P1 已 merge 的容量契约:仅在该前置成立后引用 `MAX_DURABLE_DROPPED_LOOT_ENTRIES` 与 `DroppedLootRegistry::try_insert/try_insert_batch`;超限进入统一 load-failure guard/只读降级并告警,禁止 `take(limit)` 截断、驱逐旧条目或以空 registry 覆盖数据库。在 R10 P1 未 merge 时,R3 P2 不得编译或复制临时常量。同步冻结并实现 spill/pickup persistence transaction/outbox seam:source mutation、attrited item、zone balance/qi ledger、drop insert/delete 与幂等 transaction id 构成一个 recoverable commit,且 crash/retry pins 常绿后才允许 R10 P2a 迁移 Public writer path;R10 P2b OwnerOnly private writers 另受 R10 P3、R4、R6 与 R3 P4 consumer gates 约束。 -- ⬜ P3 关服 flush + tick rebase 批次:shutdown flush registry 收编全部"节流落盘"域;绝对 tick 全部改相对基准;autosave/事件写入竞态互斥(coffin-autosave-inflight-race 模式)。 -- ⬜ P4 遗漏运行态补持久化批次:ActiveEvents、TiandaoAttention、状态效果、化虚冷却、灵眼、地表遗缴、散灵珠、可放置实体、dormant 往返身份完整性(heiwushi)等——逐个按 Slice 框架补表;P4 拆为两个独立 consumer 子批次: - - **dropped-loot hydration 子批次**:在 R10 P1 merge、R3 P2 persistence seam 与旧行 compatibility pins 就绪后,调用 `inventory::migration::migrate_legacy_dropped_loot_entry`,把旧 `dropped_loot.entry_json` 缺失字段补成 `owner = None`、`visibility = Public`,再反序列化为 `DroppedLootEntry`;此子批次先于 R6 P1 projection/page,且不等待 R10 P3/R5/R6 P4/R4。 - - **inventory-layout overflow 子批次**:仅在 R10 P3 merge 后,调用 `inventory::migration::migrate_legacy_inventory_layout`,用玩家 identity、真实机制结算点/世界 position、dimension 组装 `SpillContext`,把 `MigrationOutcome::overflow` 通过 R10 capacity API 持久化到 durable registry。 - 两类 consumer 仅在新 schema 与各自全部输出成功持久化后提交新行,缺上下文/容量/持久化或 JSON migration 失败则保留旧行并进入可重试 load guard;dropped-loot 子批次依赖 R10 P1,inventory-layout 子批次依赖 R10 P3,不得合并为一个跨越两者的门禁或另造容量常量。 +- ⬜ P1 框架落地 + 巨石拆分:保持 migration 单入口;上线 Slice guard/flush/rebase 与 inventory seam。实现 `SessionDeliveryQuota`、`SessionDeliveryReservation`、`SessionDeliveryOutbox`、receipt/disposition/history/tombstone storage 及 R1 O-01..O-27 所需 atomic CAS API。O-05 后 cancel-mark 写失败仍由原 reservation 作为 durable owner(O-25),写成功但后续取消失败则留下 `CancelPending` owner、retry metadata 与 live reconciliation scanner(O-07);只有 O-06 可释放 Q。O-08 同事务固定 payload bytes/SHA-256 digest、terminalize checkpoint 并转 reservation 为 outbox;commit 后 R1 执行 S-14,ack 丢失可从 durable 状态重放。P1 acceptance 覆盖 O-row trace、最后 row/bytes 双连接竞争、exact-max accept/+1 reject、handoff crash points、cancel failure/restart retry、payload digest、receipt/disposition atomic release。另在同阶段交付 `TsyPresence`/position/dimension routine/disconnect/shutdown coupled-snapshot crash harness;P3 仅复用,不延期首次证据。 +- **P1 obligation storage projection**:API、状态、quota effect 与 acceptance 只引用 R1 O-01..O-27;R3 不另设 lifecycle table。实现可选择独立 `CancelPending` 表或 reservation row metadata,但必须保持 O-07 durable retry owner。 +- ⬜ P2 载入守护推广:全部玩家 slice(SkillSet/Wounds/状态 buff/身份键……)收编,#1290 模式全量落地;身份主键统一。dropped-loot hydration guard 使用 R10 M-06 提供的容量/迁移 contract 与 R3 M-04 durable seam;超限进入 load-failure guard/只读降级并告警,禁止截断、驱逐或空表覆盖。spill/pickup recoverable transaction 只在对应 master M-row provider/consumer 全部存在后接入。 +- ⬜ P3 关服 flush + tick rebase 批次:shutdown flush registry 收编全部节流落盘域;绝对 tick 全部改相对基准;autosave/事件写入竞态互斥。`TsyPresence` 三者原子 autosave/disconnect/shutdown 语义已在 P1 冻结,P3 仅补全 flush registry 接线与 crash 回归。 +- ⬜ P4 遗漏运行态补持久化批次:ActiveEvents、TiandaoAttention、状态效果、化虚冷却、灵眼、地表遗缴、散灵珠、可放置实体、dormant 往返身份完整性等按 Slice 框架补表。`TsyPresence` guarded relog parity 与 placed-id hydrate 分别引用 M-12/M-05;migration consumer 只按 master ledger 进入,不复制跨轨顺序。 +- **P4 placeable gate addendum**:可放置实体子批次须落实 `plan-bughunt-placeable-entity-restart-loss-v1` P0-P2:持久化 `placed_id`(非 Entity),world/layer ready 后 hydrate `WorkbenchBlock` 并建立唯一 `placed_id→runtime Entity` registry;missing/duplicate/unhydrated fail closed。它是 R4 runtime target→stable claim 与 R1 restore rebind 的 provider,常绿前不得迁移 checkpointed workbench craft。 - ⬜ P5 bot 验收 + 吸收 plan 批量归档。 ## 吸收清单(短名省略 plan-bughunt- 前缀与 -v1 后缀) @@ -43,29 +42,27 @@ skeleton:coffin-autosave-inflight-race、identity-persist-key-mismatch、miner ## bot 验收场景 -1. `restart_player_slices`:bot 建号→修炼/学功法/受伤→关服重启→重连→断言功法/伤势/濒死后果/buff 全部还原;另以 pre-#249 inventory fixture 验证 R10 纯迁移保留全部 instance/dynamic fields、重复载入幂等并保存新 schema。 +1. `restart_player_slices`:bot 建号→修炼/学功法/受伤→关服重启→重连→断言功法/伤势/濒死后果/buff 全部还原。 2. `restart_world_runtime`:触发矿脉枯竭/配方解锁/zone influence→SIGTERM 关服→重启→断言无回滚无复活。 -3. `load_failure_guard`:基础损坏 slice 仍断言守护降级而非清零覆盖;超过 R10 P1 提供的 `MAX_DURABLE_DROPPED_LOOT_ENTRIES` 的 dropped-loot rows 只有在 R10 P1 已 merge 后执行同一 guard,数据库行数与内容不得被截断/清空;R10 前置未满足时该断言保持待接线,不得引用不存在的 symbol。另覆盖旧 `entry_json` 无 owner/visibility → `owner = None` + `Public` 的 migration/hydration 正例,以及 malformed/migration failure 保留旧行可重试;再注入 spill durable write failure,以及 pickup attrition staged 后的 commit interruption/restart,断言 attrited item、zone/ledger 与 drop delete 无单边状态,按 transaction id 重试不重复应用。 -4. `tick_rebase`:带冷却/再生倒计时重启→断言倒计时按真实流逝折算(对齐 #1289 的 deadline 折算先例)。 +3. `load_failure_guard`:基础损坏 slice 断言守护降级而非清零覆盖;仅在 R10 P1 合入后,以超过 `MAX_DURABLE_DROPPED_LOOT_ENTRIES` 的 rows 断言数据库不得截断/清空。另覆盖旧 `entry_json` 缺 owner/visibility→`None`/`Public`、malformed/migration failure 保留旧行可重试,以及 spill durable write failure、pickup attrition staged 后 commit interruption/restart;attrited item、zone/ledger 与 drop delete 不得单边提交,按 transaction id 重试不得重复应用。 +4. `tick_rebase`:带冷却/再生倒计时重启→断言倒计时按真实流逝折算(对齐 #1289 的 deadline 折算先例);另持久化一半已消耗的 `SuspensionPolicy` lease,重启/rebase 只保留原剩余 TTL,连续重复重启不刷新 lease,并覆盖剩余时长前一 tick、精确边界、后一 tick;outbox 同样覆盖 `next_retry_tick` 剩余退避、`created_at_tick` 已消耗 age、`lease_until` 剩余 lease 在单次/连续重启后的前一 tick、精确边界、后一 tick,断言不会把旧 process-local tick 直接带入新 epoch。 +5. `tsy_presence_relog_parity`:进入 TSY→关服 flush→guarded load→只有 `family_id`、`entered_at_tick`、`entry_inventory_snapshot`、`return_to`、schema/version 校验通过才 attach `TsyPresence` 并开放 TSY 请求;损坏或缺失 Slice 保持未 attach 且拒绝请求;恢复后 death-drop 对原带物继续执行 50%/武器保护,对 TSY 所得执行既有 100% 规则。 +6. `tsy_presence_snapshot_atomicity`:分别在 routine autosave、disconnect save、shutdown flush 中,向 presence、position、dimension 三个逻辑写入之间注入 crash;重启后断言三者只能全部保留旧 snapshot/version 或全部提交新 snapshot/version,不接受各 Slice 独立提交后碰巧通过 clean restore 对拍。shutdown 路径另断言 session registry 静止后才 flush coupled snapshot。 +7. `session_delivery_outbox_atomicity`:执行 R1 O-01..O-27 的 durable traces;覆盖双 connection quota race、busy loser O-05→O-25 或 O-07/O-06、restore O-04、exact-max/+1、handoff crash、lease/dead-letter、payload digest mismatch、history quota O-26、O-16/O-19 atomic release,以及 O-21→O-24 bounded retention/GC。每步断言 `quota_used = sum(active Q)`。 ## 开放问题(pre-P0 收口) 1. 载入守护的玩家体验:只读降级 vs 拒绝进服 vs 回滚到上一备份?需人工拍板。 2. 迁移链是否借机做一次 squash(v1-v39 合并基线)?风险与老存档兼容性需评估。 +3. receipt/disposition history 的 replay horizon、tombstone 上限、GC watermark 与 history quota 已由 R1 §3.1 冻结;R3 P0 只决定表结构、索引与 compaction 调度,必须执行 O-21..O-27,不能永久保留或在重启时刷新 age。 +4. TsyPresence 三者 coupled snapshot 的 routine autosave/disconnect/shutdown crash-injection harness 与跨连接屏障时序是 P1 acceptance 的组成部分:P1 必须在每个逻辑写边界注入 crash 并重启,证明结果只能全旧或全新;P3 只复用该 harness 做 flush-registry 长尾回归,不得延期首次原子性证据。 ## § P0 决议锚点(待 R3 P0 开工时补齐) -- `MAX_DURABLE_DROPPED_LOOT_ENTRIES` 的引用门:R3 P2/P4 依赖 R10 P1 merge,R3 不复制常量或在此前编译引用。 -- dropped-loot hydration consumer:R3 P4 在 R10 P1 migration helper、R3 P2 persistence seam 与旧行 compatibility pins 就绪后执行,且必须先于 R6 P1 projection/page;失败保留旧行并可重试。 -- inventory-layout migration consumer:R3 P4 的独立 overflow 子批次在 R10 P3 merge 后,使用真实 `SpillContext` 完成 overflow 持久化;失败保留旧行并可重试。 +- **R3 P2/P4** 在 master M-13/M-14 与 R10 migration/provider artifact 完成后接入 dropped-loot hydration;不复制 R10 常量,失败保留旧行可重试。 +- **R3 P1** 的 outbox/reservation 与 coupled snapshot 是 M-04/M-12 的 implementation surface;R1/R10 仅消费冻结接口。 -## 验收测试声明 +## 验收与实施边界 -- `cargo test --package bong-server persistence -- --nocapture`:Slice load guard、flush registry、tick rebase、migration consumer、dropped-loot hydration bound。 -- bot e2e:`restart_player_slices`、`restart_world_runtime`、`load_failure_guard`、`tick_rebase`。 - -## 实施边界 - -- R3 P1 只冻结接缝与框架,不提前实现 R10 inventory migration 或 dropped-loot capacity API。 -- R3 P2/P4 在依赖 merge 后才实现对应消费者;所有 migration overflow 必须有真实 spill context 和 durable sink。 -- 本 skeleton 不直接改 `server/src/inventory/**`;R10 拥有 inventory 生产 writer 与容量 API。 +- `cargo test --package bong-server persistence -- --nocapture` 覆盖 Slice guard、flush/tick rebase、migration consumer、dropped-loot bound 与 R1 O-01..O-27 durable traces;bot e2e 必须覆盖本 plan §bot 验收场景列出的全部七项:`restart_player_slices`、`restart_world_runtime`、`load_failure_guard`、`tick_rebase`、`tsy_presence_relog_parity`、`tsy_presence_snapshot_atomicity`、`session_delivery_outbox_atomicity`。任一场景未合入/未常绿,R3 P5 与下游 craft/TSY 放行均不得完成。 +- R3 P1 只冻结 inventory seam,不实现 R10 migration/capacity;P2/P4 等依赖合入后才实现 consumer,且不修改 `server/src/inventory/**`。 diff --git a/docs/plans-skeleton/plan-refactor-server-session-v1.md b/docs/plans-skeleton/plan-refactor-server-session-v1.md deleted file mode 100644 index 1d64b503e..000000000 --- a/docs/plans-skeleton/plan-refactor-server-session-v1.md +++ /dev/null @@ -1,49 +0,0 @@ -# plan-refactor-server-session-v1 — Server 交互 Session 统一生命周期框架(重构轨 R1) - -> 所属总纲:`plan-refactor-master-v1.md`。一句话:把 7 套复制粘贴的 server 端交互 session(炼丹/手搓/锻造/采集/灵田/矿脉/灵木)收敛到一个统一的 Session 生命周期框架——断线清理、跨维清理、重启恢复/显式易失声明、忙态互斥、满包产物交付,全部只写一份。 - -## 现状证据(2026-07-27 侦察) - -- 7 个功能域各自手写 session store/状态机:`alchemy/session.rs`、`craft/session.rs`、`forge/session.rs:157-208`、`gathering/session.rs:57`、`lingtian/session.rs:79-339`(6 个独立 session struct)、`mineral/session.rs`、`spiritwood/session.rs`。全仓 `SessionManager` 零命中。 -- 断线检测唯一手段 `RemovedComponents` 仅 9 个文件在用,**上述 7 个 session 模块全部不在其中**——玩家断线时进行中 session 不清理。 -- `world/dimension_transfer.rs:34` 跨维只改 `EntityLayerId`/Position,不触碰任何 session。 -- 唯一较完整的范本是 craft:`CraftSession` 是玩家实体 Component、`tick_craft_sessions` 带 `With` 过滤、有 `player_craft_sessions` 持久化表 + join 首包 idle 自愈(`network/craft_emit.rs:541,2759`)。其余 6 域既无断线清理也无持久化。 - -## 接入面 - -- **进料**:玩家实体(`With` / `RemovedComponents`)、`world/dimension_transfer`、R3 的持久化 slice 框架(flush/restore 钩子)、R4 的 gate 中间件(session 开启前置校验)。 -- **出料**:各域 session 状态 → 既有 `*_emit.rs` S2C 事件(craft_session_state 等契约不变);session 终止时的产物/材料返还 → `inventory`(满包走 R10 的统一交付垫层)。 -- **共享类型**:新 `server/src/session/`(`trait InteractionSession` + `SessionRegistry`),craft 现有行为是语义基准,不另造平行概念。 -- **跨仓库契约**:不改 wire 形状;client 端对应的 store 清理归 R2。 -- **qi_physics 锚点**:session 中断/取消涉及已扣真元的返还必须走 `qi_physics::ledger`(对齐 R5,禁止各域自写返还)。 - -## 阶段 - -- ⬜ P0 设计收口 + 吸收清单验真:逐个复读吸收清单里的 skeleton,确认仍是真缺陷;冻结 `InteractionSession` trait(生命周期钩子:`on_disconnect` / `on_dimension_change` / `on_shutdown`(接 R3) / `on_reconnect` / busy 互斥语义 / 产物交付语义);写 §N.1 决议。 -- ⬜ P1 框架落地:`server/src/session/` + craft 迁移为第一个宿主(行为不变,bot 场景锁住);断线/跨维/重启三类生命周期系统统一注册。 -- ⬜ P2 迁移批次 A:alchemy、forge、lingtian(含 #1294 在飞 skeleton 对应的 forge/lingtian session 缺陷一并消灭)。 -- ⬜ P3 迁移批次 B + 删旧:gathering、mineral、spiritwood、placed-container/tsy 容器占锁;删除各域私有生命周期代码,不留兼容层。 -- ⬜ P4 bot 验收 + 归档:新增 bot 场景全绿;被吸收 plan 批量归档(docs-only PR,Finish Evidence 指向本轨 PR + bot 场景)。 - -## 吸收清单(促升时 P0 逐个验真,短名省略 plan-bughunt- 前缀与 -v1 后缀) - -skeleton:craft-close-pause-loss、craft-session-reconnect-lock、placed-container-session-lifecycle-gap、tsy-container-disconnect-lock-leak、tsy-search-extract-concurrent-busy、world-transport-tsy-relog-presence、client-insight-offer-strand(server 侧会话悬挂部分;client 弹窗部分归 R7)、alchemy-furnace-persistence(session 持久化经 R3 钩子)、alchemy-takeback-full-inventory-loss(teardown 顺序;满包交付垫层归 R10)、forge-c2s-session-wiring、bot-handcraft-craft-outcome-timeout;在飞 #1294:forge-outcome-full-inventory-loss、lingtian-session-disconnect-server。 - -## 文件所有权与边界 - -- 独占:`server/src/session/`(新)、7 个域的 `session.rs`、`network/craft_emit.rs` 的 session tick 区。 -- 不碰:`persistence/mod.rs`(R3 域,经它暴露的钩子接入)、`client_request_handler.rs`(R4 域)、client 一切(R2/R7 域)。 -- 依赖:R3 P1(flush/restore 钩子)落地后本轨 P2 才开;P0/P1 可先行。 - -## bot 验收场景(加入 scripts/bot/scenarios/) - -1. `session_disconnect_cleanup`:bot 起炉/开工作台后断线 → 重连 → 断言 session 已清理或正确恢复(按域语义),无幽灵占锁。 -2. `session_dimension_transfer`:交互中跨维 → 断言 session 终止 + 材料按规则返还(守恒过 ledger)。 -3. `session_restart_recovery`:交互中关服重启 → 断言持久化域恢复、易失域干净终止不丢材料。 -4. `session_busy_mutex`:并发发起互斥交互(搜刮中撤离)→ 断言忙态拒绝。 -5. `session_full_inventory_delivery`:满包完成 session → 断言产物不丢(联动 R10)。 - -## 开放问题(pre-P0 收口) - -1. session 持久化的粒度:全部入 R3 slice,还是 craft 模式(表 + join 自愈)推广? -2. 中断返还的统一策略:材料全退 / 按进度折损?涉及 worldview 经济锚点,需人工拍板。 diff --git a/docs/plans-skeleton/plan-refactor-wire-s2c-v1.md b/docs/plans-skeleton/plan-refactor-wire-s2c-v1.md index 02af8d5f7..e0d63cfc4 100644 --- a/docs/plans-skeleton/plan-refactor-wire-s2c-v1.md +++ b/docs/plans-skeleton/plan-refactor-wire-s2c-v1.md @@ -14,39 +14,41 @@ - **进料**:各域游戏事件(emit 调用点)、`world` 维度/zone 信息(作用域过滤)。 - **出料**:`bong:server_data` 单通道(目标态:28 旁路全部收编或显式豁免登记);join/重连首包快照集契约(R2 清干净后靠它灌满)。 - **共享类型**:新 server `network/emit/` builder(`scope: Global | Dimension | Zone | Player`);client 桥接层唯一的枚举前缀剥离函数。 -- **跨仓库契约**:proto 形状原则上不动(收编旁路时如需并入 envelope 属破坏性变更,走 buf breaking + samples 同步;agent 侧 TS 只做被动 regenerate,不重构 agent 逻辑)。**不做双轨兼容层**——旁路收编是一次性切换。 +- **跨仓库契约**:proto 形状原则上不动;破坏性变化走 buf/samples gate,不做双轨兼容。craft domain shape 只消费 A-CS A-01..A-06 的冻结 SHA;R6 拥有 proto/Rust/client generation machinery、converter、encode/send API、bridge/router 与 emit API。contract-first artifacts 可先合入但不得宣称 live;真实 producer/consumer 切换只按 master §3/§4.1 与 PR 1902 atomic activation 执行。`workbench_key` 仅为当前进程 runtime locator,wire/store/checkpoint 不把它当 durable identity。 ## 阶段 -- ⬜ P0 设计收口 + 吸收清单验真:28 旁路逐个普查(收编 vs 豁免理由);100 emit 文件的重复模式取样归纳 builder API;枚举前缀剥离点全量清点;冻结 scope 语义与 join 首包快照集清单;正式登记 `rotate-footprint-sync`、`bot-inventory-pack-feedback` 的 inventory wire/feedback 工作,并冻结 `dropped_loot_sync` 分片 envelope(`snapshot_revision/page_index/page_count`、每页上限常量 `DROPPED_LOOT_SYNC_PAGE_SIZE = 256`)。 -- ⬜ P1 emit builder + scope 落地:builder 上线,vfx/audio/env 三类先挂 scope(跨维 bleed 立灭);跨位面切换时 env/season 全量重发;**仅在 R10 P2a 的 `DroppedLootEntry.owner/visibility` metadata provider 与 R3 P4 dropped-loot migration/hydration consumer 均合入后**,dropped-loot 内容变化与 join sync 才通过共享 builder 按固定页大小发送,同一 visibility key 的 snapshot 只排序/编码一次后复用于目标 clients,禁止 per-client 重建无界全量 payload;在上述两个前置完成前不得启用该 private projection path。 -- ⬜ P2 client 桥接层收敛:枚举前缀剥离收敛到单点(含 forge-session 修复);`ServerDataRouter` 注册表整备(分域注册文件,不再单个 1547 行 switch 追加);dropped-loot client store 仅在同 revision 全部分片收齐后原子替换,缺页/混 revision 保留旧视图并请求/等待重发。 +- ⬜ P0 设计收口 + 吸收清单验真:28 旁路逐个普查(收编 vs 豁免理由);100 emit 文件的重复模式取样归纳 builder API;枚举前缀剥离点全量清点;冻结 scope 语义与 join 首包快照集清单;登记 `rotate-footprint-sync`、`bot-inventory-pack-feedback`,并冻结 `dropped_loot_sync` 分片 envelope(`snapshot_revision/page_index/page_count`、`DROPPED_LOOT_SYNC_PAGE_SIZE = 256`)。 +- ⬜ P1 emit builder + scope、craft machinery contracts:builder 上线,vfx/audio/env 挂 scope;跨位面时 env/season 重发。对 A-CS A-01..A-06 的冻结 SHA 实现 proto/Rust mirrors、converter、samples、client encode/send API、bridge/router/store contract pins 和 `craft_emit` **API/stub**。P1 acceptance 只覆盖 R6 自有边界:两种 target、u64 decimal-string、Pause/Resume identity、Cancel、WorkbenchOpen、StateV2 phase/generation 的 wire roundtrip 与 malformed reject;不要求尚未允许存在的 R1 state producer、R4 gate 或 R7 intent producer,不宣称 production reachable。dropped-loot projection/page 只在 master M-14 artifact gate 后启用。 +- ⬜ P2 client 桥接层收敛:枚举前缀剥离收敛到单点(含 forge-session 修复);`ServerDataRouter` 注册表整备(分域注册文件,不再单个 1547 行 switch 追加);dropped-loot store 仅在同 revision 全部分片收齐后原子替换,缺页/混 revision 保留旧视图并等待重发。 - ⬜ P3 旁路归一批次:28 channel 逐批收编入 server_data envelope 或登记豁免(资源包/握手类可豁免);删除散装 receiver。 -- ⬜ P4 契约 pin 全量化:双向 sample 对拍测试补齐(113 C2S + 144 S2C 每变体至少一条正反 sample,schema 改动连 sample 一起改);emit 迁移到 builder 的长尾批次;完成 inventory receipt contract 子批次:`InventoryEventV1::Moved`(或等价 accepted receipt)必须携带 request identity、结果 revision、权威 item view,覆盖 schema/sample/convert/emit API、Fabric `InventoryEventHandler` 与 Python decoder,供 R4 handler 消费 R10 typed outcome。分片 dropped-loot 正反样本必须覆盖空/单页/恰好 256/257/末页缺失/混 revision。 +- ⬜ P4 契约 pin 全量化:从当时 authoritative registries 派生 C2S/S2C type sets,对每个实际变体至少一条正反 sample;禁止手写 116/144 充当 freshness 证据。emit 迁移长尾;inventory receipt 与 dropped-loot 样本沿用本 plan 既有 contract。 - ⬜ P5 bot 验收 + 吸收 plan 批量归档。 -R10 dropped-loot 契约优先:编码前按 recipient dimension/range/owner 投影,仅同 visibility key 复用;rejected receipt 含 reason/instance/from/to,并测两 recipient 正反可见性。 +R10 dropped-loot 契约优先:编码前按 recipient dimension/range/owner 投影,仅同 visibility key 复用;rejected receipt 携带 reason/instance/from/to,并测两 recipient 正反可见性。 ## 吸收清单(短名省略 plan-bughunt- 前缀与 -v1 后缀) active:server-data-s2c-schema-union-drift(TS union 补齐走 regenerate)、spirit-treasure-chat-key-conflict 除外(归 R7)。 -skeleton:vfx-audio-dimension-bleed、q-world-season-dimension-env-resync、forge-session-enum-unstripped(#1294 在飞)、client-request-schema-drift(C2S 契约 pin 部分)、cl-ningmai-meridian-target-drop(payload 字段丢失)、alchemy-recipe-fragment-handoff(id 前缀契约)、vfx-event-slash-contract(event_id 格式契约;agent 侧改动最小化)、npc-trade-bundle-count-bridge(展示/结算数量桥)、dropped-loot-g-pickup-range-desync(拾取范围下发对齐部分)、rotate-footprint-sync(R10 typed outcome → moved/accepted 权威 item view)、bot-inventory-pack-feedback(成功/拒绝动作级机器回执)、skillbar-cast-source-drift 与 skillconfig-castsync 除外(归 R9)。 +skeleton:vfx-audio-dimension-bleed、q-world-season-dimension-env-resync、forge-session-enum-unstripped(#1294 在飞)、client-request-schema-drift(C2S 契约 pin 部分)、cl-ningmai-meridian-target-drop(payload 字段丢失)、alchemy-recipe-fragment-handoff(id 前缀契约)、vfx-event-slash-contract(event_id 格式契约;agent 侧改动最小化)、npc-trade-bundle-count-bridge(展示/结算数量桥)、dropped-loot-g-pickup-range-desync(拾取范围下发对齐部分)、rotate-footprint-sync(R10 typed outcome → 权威 item view)、bot-inventory-pack-feedback(动作级回执)、skillbar-cast-source-drift 与 skillconfig-castsync 除外(归 R9)。 注:server↔agent 方向的桥(anticheat-tiandao-drop、niche-guardian-redis-dispatch、npc-combat-relic-schema-drift、pseudo-vein-agent-deadwire、war-participate-agent-command-drift、天道叙事簇 14 项)**不吸收**——agent 不在本次重构范围,独立保留(见总纲 §6 独立轨)。 ## 文件所有权与边界 - 独占:server `network/*_emit.rs` 公共模式与新 `network/emit/`、`schema/proto_convert.rs`;client `network/`(ProtoServerDataBridge、ServerDataRouter、BongNetworkHandler 的 channel 注册区段)。 - 不碰:`BongNetworkHandler.clearClientStateOnDisconnect` 区段(R2 域,同文件分区段,merge 前互相 fetch);`client_request_handler.rs`(R4);各 emit 的业务语义。 -- 依赖:无通用硬前置;R2 先合(同文件低冲突区段);R4 P2 建议在本轨 P1 后开。**P1 的 dropped-loot projection/page 子项例外:硬依赖 R10 P2a owner/visibility metadata provider 与 R3 P4 dropped-loot migration/hydration consumer,不能随其他 scope 子项提前实施。** +- 依赖与 production activation 只引用 master §3/§4.1 及 PR 1902。R6 P1 可在 A-CS A-01..A-06 冻结后交付 machinery contracts;R1 state producer、R4 runtime rejection、R7 intent producer 的 production evidence 分别留给其 owner phase,不构成 R6 P1 completion。dropped-loot projection/page 同理消费 master 列出的真实 provider。 ## bot 验收场景 1. `wire_scope_dimension`:主世界 bot + TSY bot 双开,主世界触发 vfx/audio→断言 TSY bot 收不到(P6 protobuf 深断言配合)。 2. `wire_dimension_transfer_resync`:bot 跨位面→断言 env/season/zone 大气全量重发。 -3. `wire_contract_sweep`:对 144 S2C 变体的 sample 对拍在 CI 常绿(配 proto-breaking 深检,联动 V 轨)。 -4. `wire_join_snapshot`:重连首包快照集完整(与 R2 的 `reconnect_state_freshness` 同场景)。 +3. `wire_contract_sweep`:对 registry-derived S2C type set 做 sample 对拍;A-06 另覆盖 phase/identity/generation wire roundtrip。runtime stale/rejection 与 producer behavior 引用 owner trace,不在本场景伪造。 +4. `wire_join_snapshot`:重连首包快照集完整;craft hydration 只在 master activation row 完成后接入,并引用 R1 S-10/S-16。 ## 开放问题(pre-P0 收口) 1. 28 旁路的收编/豁免分界(低频大 payload 如资源包显然豁免;npc bubble 这类高频小包是否值得并入 envelope)。 2. join 首包快照集的权威清单放哪维护(emit builder 注册时声明 `replay_on_join` 标志?)。 +3. production `CraftResume`、runtime-key rejection 与 real `craft_emit` activation 不在 R6 P1 验收;分别由 R7/R4/R1 owner phase 按 master atomic cutover row 提供。R6 只验 converter/API/stub contract。 +4. `CraftSessionStateV2.generation` 的 `0`、`u64::MAX`、overflow refusal 属 R6 wire pin;producer/store CAS behavior 属其 owner activation trace,不混入上游 phase。