feedback5: DESIGN §25 tidied after the CB2 / CB4 / W5AD merges — every subsection number unique and in order: §25.15 shortcuts, §25.16 CB2, §25.18 CB4 (§25.17 stays CB3's), §25.19 CB5, the language packs §25.16 → §25.20; the intro names the new streams, lists each section once (§25.15 was missing) and gets the owner's two decisions of 2026-10-07; §5.5 and §10 named among the rewritten normative sections

Text moved, not changed: a sorted-line comparison with the parent differs only in the language-pack heading and two
blank lines that the merges had lost before the CB2 and CB4 headings. No citation of the language packs' old number
outside this file (git grep); CB2's §25.16.x citations (design/network.md, tests) keep their number.
Gate: docs-only — docs-consistency, docs-paths, package, feedback1-gaps 53 / 53 (the tests that read the design
docs); code unchanged since the W5AD merge's full gate.
This commit is contained in:
sganggs committed 2026-10-07 03:29:28 +08:00
1 parent 66cdf493fc
commit 37e7f0020c
1 file changed
+139 -133
+139 -133
View File
@@ -9,12 +9,14 @@ later merges of feedback4 brought the rest of 0.1.4 in. The workstreams, each on
after review with its golden scenarios named: R1–R6 (refactor, tooling), W5A (golden results), W5B / W5D / W5G /
K1–K3 / W5N (补位), W5H / W5K / O1–O24 (自选编队 and its operator kits), W5I / W5S (languages, phases 1 and 2), W5C /
W5E / W5F / W5J / W5L (fidelity, wave 1), WE1 / WE2 (the follow-ups the kit waves reported), W5M, W5R and the feedback4
merges (the 0.1.4 ports), W5V (the owner's items of 2026-10-06), W5U (packaging), W5T (the docs) and W5Y (round 18:
three GitHub issues and three community PRs). Each rule was
checked against the official data and PRTS; anything no source backs is marked [ASSUMED].
merges (the 0.1.4 ports), W5V (the owner's items of 2026-10-06), W5U (packaging), W5T (the docs), W5Y (round 18:
three GitHub issues and three community PRs), W5AA and CB2 / CB4 / CB5 (the community reports of 2026-10-06), W5AC
(rebindable shortcuts) and W5AD (language packs). Each rule was checked against the official data and PRTS; anything no
source backs is marked [ASSUMED].
The normative sections were rewritten with the streams: §0 (the DIY slots), §2 (the layout), §5.5 (the enemy damage
frame, the block-count target rule), §5.6 (the trigger rules), §8.1 / §8.3 (`welcome.diyKitted`, `standIns`, `diy`,
`diyBanned`), §16 (干员持有, 自选编队). Where each is handled: refactor and tooling → §25.1; Chinese and English →
frame, the block-count target rule, 自制投石机's 索敌不受阻挡影响), §5.6 (the trigger rules), §8.1 / §8.3
(`welcome.diyKitted`, `standIns`, `diy`, `diyBanned`), §10 (the rebindable shortcuts), §16 (干员持有, 自选编队). Where each
is handled: refactor and tooling → §25.1; Chinese and English →
§25.2; 补位 → §25.3; 自选编队 and the 71 operator kits → §25.4; trigger rules (the larger-range rule, AUTO skills) →
§25.5; fidelity fixes of wave 1 (GitHub #41, #49, #82, #96 / #137, #107, #116; 伺夜's 狼群; Touch) → §25.6; WE1, sim
rules (GitHub #181, #187 / #170, follow-ups) → §25.7; WE2, data / match / UI (follow-ups, GitHub #148) → §25.8; the
@@ -25,17 +27,19 @@ settled (泥岩's 闭锁; 忍冬, 深靛, 阿) → §25.11; release packaging an
with no Spine model) → §25.12; round 18 — 拟态物质 at the copy cap (GitHub #207), 华法琳's 7 / 14 (GitHub #175, PR #192),
a merged elite 拉普兰德 as a new one (GitHub #169), the leader pool per player alive (PR #209), the flying lift and two
model quirks (PR #211) → §25.13; the community reports of 2026-10-06 (the 孤岛风云 prisoners drawn freed, 忍冬's trigger, a
bounty burned to death by the 活性源石, 枯朽之种 paying a bounty) → §25.14; the second batch of community reports of
2026-10-06, stream CB5 (match flow and UI: a module's 特性追加 line after the class trait, the new shop slot filled at the
upgrade, an eliminated player following a player through every round, the boss field and its leader in a watcher's boss
prep, the pair together in the boss prep with the official green frames; PRs #189 / #191 reused, not merged; the
battle-history and hotkey requests left to the owner) → §25.19; the community reports of 2026-10-06, stream CB2 (healers, summons, timers: 刺玫's S2 heals, 琳琅诗怀雅 healing herself and her 破财消灾 cost after a
redeploy, 赫默's drone at full SP, the countdown summons' 无敌 / 禁疗 / timer bar, stacked alchemy zones; the chain heal,
DoT damage, 伺夜's 狼影 and the 吟游者 trait answered) → §25.16; the community reports of 2026-10-06, stream CB4 (enemies, bosses, waves, movement: the 突袭 landing on the 临时整备区 row and the other half of the
联防 map, a silenced 萨卡兹枯朽战士's death pollution, 萨卡兹穿刺手's speed after a stun and its first hit, 自制投石机 blocked on the 排气格栅; the briefing types'
7–9 waves, 新硎's 断刃, a freed prisoner in 联防 and 鸭爵's two swaps in one wave already answered; the moving leaders' routes
checked; 野鬃, 锏's pulls, 昆图斯's footprint and the leaders' attack rate not reproduced) → §25.18; the rebindable shortcuts
→ §25.15; language packs, the first type of content packs (a language is a file in the language folder) → §25.16.
bounty burned to death by the 活性源石, 枯朽之种 paying a bounty) → §25.14; the rebindable shortcuts → §25.15; the
second batch of community reports of 2026-10-06, one section per stream: CB2, healers, summons, timers (刺玫's S2 heals,
琳琅诗怀雅 healing herself and her 破财消灾 cost after a redeploy, 赫默's drone at full SP, the countdown summons' 无敌 /
禁疗 / timer bar, stacked alchemy zones; the chain heal, DoT damage, 伺夜's 狼影 and the 吟游者 trait answered) → §25.16;
CB4, enemies, bosses, waves, movement (the 突袭 landing on the 临时整备区 row and the other half of the 联防 map, a
silenced 萨卡兹枯朽战士's death pollution, 萨卡兹穿刺手's speed after a stun and its first hit, 自制投石机 blocked on the
排气格栅; the briefing types' 7–9 waves, 新硎's 断刃, a freed prisoner in 联防 and 鸭爵's two swaps in one wave already
answered; the moving leaders' routes checked; 野鬃, 锏's pulls, 昆图斯's footprint and the leaders' attack rate not
reproduced) → §25.18; CB5, match flow and UI (a module's 特性追加 line after the class trait, the new shop slot filled at
the upgrade, an eliminated player following a player through every round, the boss field and its leader in a watcher's
boss prep, the pair together in the boss prep with the official green frames; PRs #189 / #191 reused, not merged; the
battle-history and hotkey requests left to the owner) → §25.19; language packs, the first type of content packs (a
language is a file in the language folder) → §25.20.
Decisions of the owner: 2026-10-04 — the server stays on plain `node:http` + `ws`, no web framework (§25.1);
2026-10-05 — the 补位 plan (§25.3); the 自选编队 rules, the collab operators out of the pool, ISW-A refused (§25.4);
@@ -48,8 +52,8 @@ a melee 突袭 member lands where it can block (§25.11); 华法琳's granted tr
lock (§25.13.2); a newly merged elite 拉普兰德 counts as a new 拉普兰德 (§25.13.3); the leader pool = bloodPoint × the players
alive at the fight's start, adopting PR #209 and replacing 「保持固定血量」 (§25.13.4); flying units at 1.3 tiles and the two
model quirks of PR #211 (§25.13.5); the bounty pays whenever its enemy dies (relayed with the community
reports, §25.14.3); 2026-10-07 — a language is a file in the language folder, and the language pack is the first type
of a general content-pack mechanism (§25.16).
reports, §25.14.3); 2026-10-07 — the in-match shortcuts rebindable in 设置 (§25.15); a language is a file in the
language folder, and the language pack is the first type of a general content-pack mechanism (§25.20).
### 25.1 Refactor and tooling: the big files split, golden results, lint, types, import boundaries (refactor plan; owner's decision of 2026-10-04: no web framework) — `server/http/`, `server/match/match/`, `server/match/player/`, `server/sim/battle/`, `content/enemies/`, `kits/ops/`, `public/js/ui/gameLogic/`, `render/fx/`, `render/app/`, `screens/game/`, `tools/golden.mjs`, `tools/check-imports.mjs`, `eslint.config.js`, `jsconfig.json`
@@ -1104,120 +1108,6 @@ fixed (W5AA's commits name every golden scenario moved); two were already fixed
keyboard only in the mock match — Q then does nothing, W retreats, the choice survives a reload; phone width). Docs:
docs/PLAYING.md §11, README 操作, DESIGN §10 (design/client.md).
### 25.19 Community reports of 2026-10-06, stream CB5: match flow and UI — `ui/loadoutModel.js traitLines`, `player/economy.js _openLevelSlots`, `match/watch.js _followScout`, `match/views.js prepFieldMeta / bossMateView`, `render/app.js syncMates`, `ui/gameLogic/camera.js teamFrameIds`
CB5 took the items about the match flow and the screens (16.2, 19, 51, 55, 56 of the list; 22 and 48 are features the
owner has not decided — what each would take is at the end). Each was reproduced first (a script, or the real server in
headless Chrome for the screens), checked against the official data, PRTS and the official tutorial pages, then fixed in
one commit with its tests; two open community PRs were reused without merging them (§25.19.6).
#### 25.19.1 「干员调配界面很多干员的某个模组只有额外特性没有基础特性,不清楚是文本错误还是实际效果也有错」 (item 16.2): text only — the module's added line now follows the class trait — `ui/loadoutModel.js traitLines / fullTraitText`, `ui/detailPanel.js traitText`, `screens/loadout.js ModuleInfo`, `screens/diy.js ModuleRow`
- **Seen**: 114 of the 164 modules the 干员调配 screen offers (128 of all 184 elite module records) showed under 特性 only
the module's extra line — 隐现's MAR-X read 「攻击空中单位时攻击力提升至110%」 without 「优先攻击空中单位」; 局内数值, the
in-match detail card and the 自选 module row did the same.
- **Official**: a module's trait candidate either rewrites the class trait (`overrideDescripton`) or adds a line after it
(`additionalDescription`); PRTS flags the latter 「特性追加」 on every such module (e.g. 伊芙利特 BLA-X / BLA-Δ, 玛恩纳
LIB-X, 莫斯提马 SPC-X / SPC-Y).
- **Cause**: the data keeps both lines (`traitOverride.desc` = the class trait or its rewrite, `moduleDesc` = the added
line); the screens showed `moduleDesc` instead of `desc` (since 0.1.2: the review of GitHub #64 in §22 picked the
module line over a cloned record's class trait). The battle was right: for every added-line module the unit keeps the class
trait — the same trait text, targeting, attack shape and trait blackboard as 不装备 (圣约送葬人 REA-Y also rewrites it).
- **Now**: `traitLines` / `fullTraitText`: the class trait (or the rewrite), then the added line; the module card shows
them as 特性 and 特性追加 (en "Added Trait").
- Golden: unchanged (no sim change). Tests: `test/ui/feedback5-module-trait.test.js` (6; the text cases fail on the
parent, the data and sim pins pass there), `test/ui/loadout-stats.test.js` updated; SP_E2E `loadout.e2e` 9 / 9.
Docs: docs/DATA.md (`trait`, `traitOverride`).
#### 25.19.2 「升级商店获得新的商店位时用新卡补上,而不是空着」 (item 19): the upgrade opens the new slot with a new card — `player/economy.js _openLevelSlots`, `audit.js`
- **Seen**: a level-up from 1 to 2 (险境: 3 → 4 operator slots) or 3 to 4 (4 → 5; 标准 5 → 6) showed no new card until the
next refresh or round start (the shop kept its rolled layout; META listed it as an assumption).
- **Official**: the tutorial's 休整期 page (local guide art `autochess_home_4`): 「升级:消耗资金升级调度中心。升级后将出现更多
的商品栏位、可调度干员以及新装备」; PRTS 帮助 「消耗资金可以为调度中心升级,从而增加刷新栏位」.
- **Now**: `levelUp` → `_openLevelSlots`: the new level's extra slots are appended to the operator slots at once (the
item slot stays after them), each drawn at the new level; the cards shown stay in place, a bought card stays sold.
`audit.js` checks the count and the kept cards (`matchrun --check`: no violation).
- [ASSUMED]: the new card follows the freeze toggle, as a manual refresh's cards do.
- Golden: matches — all 18 scenarios (every bot levels its shop and now sees, and draws the shop RNG for, the new card at
once); the battle families unchanged. Tests: `test/match/feedback5-shop-levelslots.test.js` (3, all fail on the
parent). Docs: docs/META.md §3 / §7, docs/PLAYING.md.
#### 25.19.3 「死亡观战时每一回合结束都会被转回自己家」 (item 56): an eliminated player follows a player through every phase reset — `match/watch.js watchPref / _watchTargetOf / _followScout`, `battle/observe.js followedScout`
- **Seen** (real server, headless Chrome): every round start cleared an eliminated player's watch — in 回合开始 / 机变 /
休整期 it saw its own empty board, each battle started on the first field; a spectator seat asked for the first player
itself.
- **Official**: research 09 §3.1 — after elimination the client keeps observing (self_dead_dialog KEEP_WATCH, the
server's DeadAutoObDn { obIndex, state, preparation }, FindFirstAvailObTarget).
- **Now**: `Match.watchPref` = the player of the viewer's last manual `g.watch` (a prep scout, a single-player field, or
— new, optional — `g.watch { playerId }` naming the row tapped on a shared 联防 / boss pair field); `_watchTargetOf` = that
player while still in, else the first player still in (seat order, AI seats included). The round start pushes its prep
board (`_followScout`, kept through 机变 and 休整期), both combat modes and the boss fields start on its field, a resync or
a spectator joining in prep gets the board, a followed player who quits hands over at once. The screen adopts the pushed
board like a 前往查看 tap (`followedScout`; PR #189's stale-field fix) and keeps it through the prep phases; 返回自己 asks
the server for nothing (the viewer has no board).
- Golden: unchanged. Tests: `test/match/feedback5-watch-follow.test.js` (9; 8 fail on the parent's server),
`test/match/spectator.test.js` updated. Docs: design/network.md, docs/PLAYING.md.
#### 25.19.4 「死亡后观战视角看不到场上的关底boss」 (item 55): a watcher of a boss round's prep sees the boss field and its leader — `match/views.js prepFieldMeta`, `screens/game.js`
- **Seen** (headless Chrome, the guest eliminated at the boss round): in the Final Assault fight the watched boss field
showed the leader (enemy_9013_acstmk at 10,3); in the boss round's prep the guest saw its own empty normal board (camera
'prep', no leader) and a scout of a teammate was the normal board too — while the players prepare on their half of the
boss field with the leader standing there (§23.18, community report #12).
- **Now**: in a boss round's prep the scout is built on the boss field — `kind: 'boss'`, `BOSS_RECT`, the scouted player's
pieces through `bossFieldPlacement` (rows − 7, the right half mirrored with RIGHT ↔ LEFT), `side`, the leader's spawn in
`nextEnemies` — and framed by the boss-field prep camera of that side, which shows the leader. With §25.19.3 the
eliminated player gets it at the round start; the same run after the fix: field 'boss', camera bossPrep, leader shown.
- Golden: unchanged. Tests: `test/match/feedback5-boss-prep-scout.test.js` (2, fail on the parent); SP_E2E
`test/ui/feedback5-boss-pair.e2e.test.js` (item 55 case). Docs: design/network.md, design/client.md §15.
#### 25.19.5 「还原原版最终boss战准备阶段:像战斗中那样把会成一组的两个玩家放在一起…同组两个玩家屏幕左边的头像也像原版那样用绿框框起来」 (item 51): the pair together in the boss prep, the official green frames — `match/views.js bossMateView`, `match/messaging.js markPrivate`, `render/app.js syncMates`, `ui/teamPanel.js`, `ui/gameLogic/camera.js teamFrameIds`
- **Seen**: the own half was already the official one — the right-hand player on the right half with the
right_boss_*_camera_param camera, its layout mirrored col c → 20 − c with RIGHT ↔ LEFT, so its operators face the leader
as before (research 09 §1.2, §15) — but the partner's half stood empty and nothing framed the pair.
- **Official**: every client knows every board (ChangePositionDn { boardStatus }, research 09); the team list frames the
co-op group (the owner's recall; the client's ui/battle `bg_team_border`, a white 100×100 frame with its glow, and the
autochess green of `icon_self`, #4ed8af).
- **Now**: in a boss round's prep `m.private.bossMate { playerId, side, units }` carries the partner's board on its half,
placed as the battle will (bossFieldPlacement); a partner's change marks the player's m.private too (`markPrivate`), so
it follows live; the own prep view draws those units read-only ('m:<uid>' views, never picked or dragged) and lights the
whole boss field; a scout of either player shows both halves (`mate`). The team panel frames the pair's two avatars
(`teamFrameIds`: the players still in by seat, two by two, as `prepCamera`; the boss field in the fight) from the round
start through the fight, with the official sprite tinted green (a plain ring without the local art); a lone player, an
eliminated player and a spectator seat get none; 联防 is not framed (not asked).
- [ASSUMED]: the partner's bench is not shown (its board only); the frame's exact official geometry (one frame per avatar).
- Golden: unchanged. Tests: `test/match/feedback5-boss-pair-prep.test.js` (4, fail on the parent), SP_E2E
`test/ui/feedback5-boss-pair.e2e.test.js` (both halves, the mirrored right half, the frames in prep and fight).
Docs: design/client.md §10 / §15, design/network.md, docs/PLAYING.md.
#### 25.19.6 Overlap with the open community PRs #189 and #191 (not merged; their authors' fixes were pending)
- PR #189 (watch preference, @2321Robin) — §25.19.3 builds the same chain (a manual watch is the preference; round start,
both combat modes, the boss fields and the resync follow it; the stale-field fix of the adopted scout), on the 0.2.0
method containers. Differences: the fallback is the first player still in with AI seats included (a lone human with AI
teammates had nobody to follow — the report's case), and a shared field records the `playerId` the row tap sends (the
review's point 2) instead of nothing.
- PR #191 (same-field frames, issue #190) — §25.19.5 frames the pair as the owner asked: green, the official sprite, both
avatars (self included), the boss rounds only; the pairing is read from m.public as the prep camera already does, so no
`bossPairing` field; #191's 联防 frames and the phase banner naming the partner are not built. If the owner still wants
them, they can go on top of this.
#### 25.19.7 Not built — what the two feature requests would take (the owner has not decided)
- **22 战绩 / 分队胜率**: the result is already complete (`m.result`: per-player stats, lineup, band, bonds, titles, rounds
passed, victory — `server/match/results.js`). With no accounts the simplest is per browser: append a compact record at
`m.result` to localStorage (capped, e.g. the last 200; only matches of ≥ 2 rounds, as asked), a 战绩 screen in the lobby
(list, win rate per strategy / core bond / mode and difficulty, 导出 / 导入 like the loadout), tests and i18n — no
server change. Server-side history needs persistence and an identity the remake does not have. Decisions: browser-only
or not; what 「分队」 groups by (strategy, core bond, lineup); the sample cap.
- **48 自定义快捷键**: the keys are fixed in `ui/gameLogic/shortcuts.js shortcutFor` (R 刷新, F 冻结, D 升级, Q 撤退, X 出售,
Space 准备 / 暂停, Esc). It would take a 快捷键 section in 设置 (capture a key per action, conflicts refused, 恢复默认),
a sanitised map in the settings (`sanitizeSettings`), `shortcutFor` reading it, every `<kbd>` hint and tooltip reading it
too, i18n and tests. Decision: whether to allow it at all (the official PC client's keys are fixed as far as the data
shows: `pc_key_holder` hints).
### 25.16 Community reports of 2026-10-06, stream CB2: healers, summons, timers — `kits/ops/chess_char_1_06-vendla.js`, `chess_char_3_04-swire2.js`, `chess_char_2_02-silent.js`, `content/tokens.js COUNTDOWN_SUMMONS / startCountdown`, `sim/snapshot.js unitTuple`, `render/fx/zones.js zoneAlpha`
The owner's second collection of community reports (2026-10-06/07) was split over five streams; CB2 took the healers,
@@ -1313,6 +1203,7 @@ the evidence.
trait is 生命回复速度, which 禁疗 and 无法被友方治疗 do not stop (§25.6.8): 狼群 440 → 532 and 斥罪 1480 → 1618 HP in 3 s
under 魔王 (pinned by `test/content/feedback5-regen.test.js`); 孤立 units stay out (PRTS 孤立). The countdown summons
cannot be refilled since §25.16.5.
### 25.18 Community reports of 2026-10-06, stream CB4: enemies, bosses, waves, movement — `sim/battle/players.js onOwnBoard`, `content/bonds/addon/battle.js raidTile`, `content/enemies/archetypes.js kitPolluted`, `content/enemies/special.js kitRush`, `targeting.js canTargetAlly`
The owner's second collection of community reports (2026-10-06/07) was split over five streams; CB4 took the items about
@@ -1507,7 +1398,122 @@ briefing types' share of the waves).
卢西恩 attacks only its blocker and blinks past it. A leader and a situation from the reporter would show what differs.
- Golden: unchanged. Pinned: `test/sim/feedback5-cb4-pins.test.js` (the four interval leaders at ≥ max − 1 attacks).
### 25.16 Language packs: a language is a file in the language folder, the first type of content packs (W5AD; the owner's decisions of 2026-10-07) — `shared/packs.js`, `shared/i18nPacks.js`, `shared/i18n.js`, `shared/i18nData.js`, `server/packs.js`, `server/http/static.js`, `public/js/ui/lang.js`, `public/js/data.js`, `tools/i18n.mjs`, `tools/packs.mjs`, `tools/build-i18n.mjs`, `tools/package.mjs`, docs/I18N.md §5, docs/PACKS.md
### 25.19 Community reports of 2026-10-06, stream CB5: match flow and UI — `ui/loadoutModel.js traitLines`, `player/economy.js _openLevelSlots`, `match/watch.js _followScout`, `match/views.js prepFieldMeta / bossMateView`, `render/app.js syncMates`, `ui/gameLogic/camera.js teamFrameIds`
CB5 took the items about the match flow and the screens (16.2, 19, 51, 55, 56 of the list; 22 and 48 are features the
owner has not decided — what each would take is at the end). Each was reproduced first (a script, or the real server in
headless Chrome for the screens), checked against the official data, PRTS and the official tutorial pages, then fixed in
one commit with its tests; two open community PRs were reused without merging them (§25.19.6).
#### 25.19.1 「干员调配界面很多干员的某个模组只有额外特性没有基础特性,不清楚是文本错误还是实际效果也有错」 (item 16.2): text only — the module's added line now follows the class trait — `ui/loadoutModel.js traitLines / fullTraitText`, `ui/detailPanel.js traitText`, `screens/loadout.js ModuleInfo`, `screens/diy.js ModuleRow`
- **Seen**: 114 of the 164 modules the 干员调配 screen offers (128 of all 184 elite module records) showed under 特性 only
the module's extra line — 隐现's MAR-X read 「攻击空中单位时攻击力提升至110%」 without 「优先攻击空中单位」; 局内数值, the
in-match detail card and the 自选 module row did the same.
- **Official**: a module's trait candidate either rewrites the class trait (`overrideDescripton`) or adds a line after it
(`additionalDescription`); PRTS flags the latter 「特性追加」 on every such module (e.g. 伊芙利特 BLA-X / BLA-Δ, 玛恩纳
LIB-X, 莫斯提马 SPC-X / SPC-Y).
- **Cause**: the data keeps both lines (`traitOverride.desc` = the class trait or its rewrite, `moduleDesc` = the added
line); the screens showed `moduleDesc` instead of `desc` (since 0.1.2: the review of GitHub #64 in §22 picked the
module line over a cloned record's class trait). The battle was right: for every added-line module the unit keeps the class
trait — the same trait text, targeting, attack shape and trait blackboard as 不装备 (圣约送葬人 REA-Y also rewrites it).
- **Now**: `traitLines` / `fullTraitText`: the class trait (or the rewrite), then the added line; the module card shows
them as 特性 and 特性追加 (en "Added Trait").
- Golden: unchanged (no sim change). Tests: `test/ui/feedback5-module-trait.test.js` (6; the text cases fail on the
parent, the data and sim pins pass there), `test/ui/loadout-stats.test.js` updated; SP_E2E `loadout.e2e` 9 / 9.
Docs: docs/DATA.md (`trait`, `traitOverride`).
#### 25.19.2 「升级商店获得新的商店位时用新卡补上,而不是空着」 (item 19): the upgrade opens the new slot with a new card — `player/economy.js _openLevelSlots`, `audit.js`
- **Seen**: a level-up from 1 to 2 (险境: 3 → 4 operator slots) or 3 to 4 (4 → 5; 标准 5 → 6) showed no new card until the
next refresh or round start (the shop kept its rolled layout; META listed it as an assumption).
- **Official**: the tutorial's 休整期 page (local guide art `autochess_home_4`): 「升级:消耗资金升级调度中心。升级后将出现更多
的商品栏位、可调度干员以及新装备」; PRTS 帮助 「消耗资金可以为调度中心升级,从而增加刷新栏位」.
- **Now**: `levelUp` → `_openLevelSlots`: the new level's extra slots are appended to the operator slots at once (the
item slot stays after them), each drawn at the new level; the cards shown stay in place, a bought card stays sold.
`audit.js` checks the count and the kept cards (`matchrun --check`: no violation).
- [ASSUMED]: the new card follows the freeze toggle, as a manual refresh's cards do.
- Golden: matches — all 18 scenarios (every bot levels its shop and now sees, and draws the shop RNG for, the new card at
once); the battle families unchanged. Tests: `test/match/feedback5-shop-levelslots.test.js` (3, all fail on the
parent). Docs: docs/META.md §3 / §7, docs/PLAYING.md.
#### 25.19.3 「死亡观战时每一回合结束都会被转回自己家」 (item 56): an eliminated player follows a player through every phase reset — `match/watch.js watchPref / _watchTargetOf / _followScout`, `battle/observe.js followedScout`
- **Seen** (real server, headless Chrome): every round start cleared an eliminated player's watch — in 回合开始 / 机变 /
休整期 it saw its own empty board, each battle started on the first field; a spectator seat asked for the first player
itself.
- **Official**: research 09 §3.1 — after elimination the client keeps observing (self_dead_dialog KEEP_WATCH, the
server's DeadAutoObDn { obIndex, state, preparation }, FindFirstAvailObTarget).
- **Now**: `Match.watchPref` = the player of the viewer's last manual `g.watch` (a prep scout, a single-player field, or
— new, optional — `g.watch { playerId }` naming the row tapped on a shared 联防 / boss pair field); `_watchTargetOf` = that
player while still in, else the first player still in (seat order, AI seats included). The round start pushes its prep
board (`_followScout`, kept through 机变 and 休整期), both combat modes and the boss fields start on its field, a resync or
a spectator joining in prep gets the board, a followed player who quits hands over at once. The screen adopts the pushed
board like a 前往查看 tap (`followedScout`; PR #189's stale-field fix) and keeps it through the prep phases; 返回自己 asks
the server for nothing (the viewer has no board).
- Golden: unchanged. Tests: `test/match/feedback5-watch-follow.test.js` (9; 8 fail on the parent's server),
`test/match/spectator.test.js` updated. Docs: design/network.md, docs/PLAYING.md.
#### 25.19.4 「死亡后观战视角看不到场上的关底boss」 (item 55): a watcher of a boss round's prep sees the boss field and its leader — `match/views.js prepFieldMeta`, `screens/game.js`
- **Seen** (headless Chrome, the guest eliminated at the boss round): in the Final Assault fight the watched boss field
showed the leader (enemy_9013_acstmk at 10,3); in the boss round's prep the guest saw its own empty normal board (camera
'prep', no leader) and a scout of a teammate was the normal board too — while the players prepare on their half of the
boss field with the leader standing there (§23.18, community report #12).
- **Now**: in a boss round's prep the scout is built on the boss field — `kind: 'boss'`, `BOSS_RECT`, the scouted player's
pieces through `bossFieldPlacement` (rows − 7, the right half mirrored with RIGHT ↔ LEFT), `side`, the leader's spawn in
`nextEnemies` — and framed by the boss-field prep camera of that side, which shows the leader. With §25.19.3 the
eliminated player gets it at the round start; the same run after the fix: field 'boss', camera bossPrep, leader shown.
- Golden: unchanged. Tests: `test/match/feedback5-boss-prep-scout.test.js` (2, fail on the parent); SP_E2E
`test/ui/feedback5-boss-pair.e2e.test.js` (item 55 case). Docs: design/network.md, design/client.md §15.
#### 25.19.5 「还原原版最终boss战准备阶段:像战斗中那样把会成一组的两个玩家放在一起…同组两个玩家屏幕左边的头像也像原版那样用绿框框起来」 (item 51): the pair together in the boss prep, the official green frames — `match/views.js bossMateView`, `match/messaging.js markPrivate`, `render/app.js syncMates`, `ui/teamPanel.js`, `ui/gameLogic/camera.js teamFrameIds`
- **Seen**: the own half was already the official one — the right-hand player on the right half with the
right_boss_*_camera_param camera, its layout mirrored col c → 20 − c with RIGHT ↔ LEFT, so its operators face the leader
as before (research 09 §1.2, §15) — but the partner's half stood empty and nothing framed the pair.
- **Official**: every client knows every board (ChangePositionDn { boardStatus }, research 09); the team list frames the
co-op group (the owner's recall; the client's ui/battle `bg_team_border`, a white 100×100 frame with its glow, and the
autochess green of `icon_self`, #4ed8af).
- **Now**: in a boss round's prep `m.private.bossMate { playerId, side, units }` carries the partner's board on its half,
placed as the battle will (bossFieldPlacement); a partner's change marks the player's m.private too (`markPrivate`), so
it follows live; the own prep view draws those units read-only ('m:<uid>' views, never picked or dragged) and lights the
whole boss field; a scout of either player shows both halves (`mate`). The team panel frames the pair's two avatars
(`teamFrameIds`: the players still in by seat, two by two, as `prepCamera`; the boss field in the fight) from the round
start through the fight, with the official sprite tinted green (a plain ring without the local art); a lone player, an
eliminated player and a spectator seat get none; 联防 is not framed (not asked).
- [ASSUMED]: the partner's bench is not shown (its board only); the frame's exact official geometry (one frame per avatar).
- Golden: unchanged. Tests: `test/match/feedback5-boss-pair-prep.test.js` (4, fail on the parent), SP_E2E
`test/ui/feedback5-boss-pair.e2e.test.js` (both halves, the mirrored right half, the frames in prep and fight).
Docs: design/client.md §10 / §15, design/network.md, docs/PLAYING.md.
#### 25.19.6 Overlap with the open community PRs #189 and #191 (not merged; their authors' fixes were pending)
- PR #189 (watch preference, @2321Robin) — §25.19.3 builds the same chain (a manual watch is the preference; round start,
both combat modes, the boss fields and the resync follow it; the stale-field fix of the adopted scout), on the 0.2.0
method containers. Differences: the fallback is the first player still in with AI seats included (a lone human with AI
teammates had nobody to follow — the report's case), and a shared field records the `playerId` the row tap sends (the
review's point 2) instead of nothing.
- PR #191 (same-field frames, issue #190) — §25.19.5 frames the pair as the owner asked: green, the official sprite, both
avatars (self included), the boss rounds only; the pairing is read from m.public as the prep camera already does, so no
`bossPairing` field; #191's 联防 frames and the phase banner naming the partner are not built. If the owner still wants
them, they can go on top of this.
#### 25.19.7 Not built — what the two feature requests would take (the owner has not decided)
- **22 战绩 / 分队胜率**: the result is already complete (`m.result`: per-player stats, lineup, band, bonds, titles, rounds
passed, victory — `server/match/results.js`). With no accounts the simplest is per browser: append a compact record at
`m.result` to localStorage (capped, e.g. the last 200; only matches of ≥ 2 rounds, as asked), a 战绩 screen in the lobby
(list, win rate per strategy / core bond / mode and difficulty, 导出 / 导入 like the loadout), tests and i18n — no
server change. Server-side history needs persistence and an identity the remake does not have. Decisions: browser-only
or not; what 「分队」 groups by (strategy, core bond, lineup); the sample cap.
- **48 自定义快捷键**: the keys are fixed in `ui/gameLogic/shortcuts.js shortcutFor` (R 刷新, F 冻结, D 升级, Q 撤退, X 出售,
Space 准备 / 暂停, Esc). It would take a 快捷键 section in 设置 (capture a key per action, conflicts refused, 恢复默认),
a sanitised map in the settings (`sanitizeSettings`), `shortcutFor` reading it, every `<kbd>` hint and tooltip reading it
too, i18n and tests. Decision: whether to allow it at all (the official PC client's keys are fixed as far as the data
shows: `pc_key_holder` hints).
### 25.20 Language packs: a language is a file in the language folder, the first type of content packs (W5AD; the owner's decisions of 2026-10-07) — `shared/packs.js`, `shared/i18nPacks.js`, `shared/i18n.js`, `shared/i18nData.js`, `server/packs.js`, `server/http/static.js`, `public/js/ui/lang.js`, `public/js/data.js`, `tools/i18n.mjs`, `tools/packs.mjs`, `tools/build-i18n.mjs`, `tools/package.mjs`, docs/I18N.md §5, docs/PACKS.md
- **Seen**: after §25.2 English was wired in by name — the language list `['zh', 'en']`, the switch's labels, English
checks in the title screen, `fmtNum`, the tab title, a CSS rule, the overlay URL pattern — so another language meant