diff --git a/CHANGELOG.md b/CHANGELOG.md index 69d1340..5060773 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # Changelog +## alpha 0.6.8.1015 + +- Add an atomic compatibility path for old active CS autochess runs. When a + group still has a pre-schema-3 active tournament, `/自走棋开始` now archives + that old run as cancelled and opens a fresh schema-3 season in the same + SQLite transaction. +- Preserve old story and battle archive rows while ensuring the new season is + the only active autochess run selected by future commands. + ## alpha 0.6.7.1015 - Expand CS Major autochess content to 100+ player cards and an 18-coach diff --git a/README.md b/README.md index 132e621..d1b33cd 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ JWeBot 是运行于 Linux 的单群微信自动回复系统。它通过本地 OC 观察选定的微信窗口,将消息写入持久化 Edge 账本,调用经过审核的命令插件, 并通过带视觉确认的 GUI 发送链路回复。 -当前版本:`alpha 0.6.7.1015`。 +当前版本:`alpha 0.6.8.1015`。 0.6.1 新增本地上下文总结、人物与别名索引、单人虚拟金币游戏,并增加 两秒画面复核以补抓遗漏的消息变化。命令和游戏管理规则见 @@ -33,6 +33,9 @@ JWeBot 是运行于 Linux 的单群微信自动回复系统。它通过本地 OC 18 人;训练赛改为按参赛人数生成的真实对阵轮次;单图比分由 MR15 逐回合模拟产生,不再使用固定比分档;赛事叙事素材扩充到 120+ 条, D 牌、锁店、调阵容和战术窗口会在轮次之间自然提示。 +0.6.8 增加旧版自走棋 active 状态兼容:如果群里残留 0.6.6/0.6.7 +之前创建的未完成赛事,新的 `/自走棋开始` 会原子归档旧档案并开启新版 +赛季,不需要维护人员手工改库。 ### 运行流程 @@ -275,7 +278,7 @@ visually confirmed GUI bridge. Current version: ```text -alpha 0.6.7.1015 +alpha 0.6.8.1015 ``` ## Runtime Flow diff --git a/VERSION b/VERSION index a76aa17..d08e84a 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -alpha 0.6.7.1015 +alpha 0.6.8.1015 diff --git a/packages/jwebot-config/pyproject.toml b/packages/jwebot-config/pyproject.toml index a36b613..b3a4940 100644 --- a/packages/jwebot-config/pyproject.toml +++ b/packages/jwebot-config/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "jwebot-config" -version = "0.6.7a0+1015" +version = "0.6.8a0+1015" description = "Profile-based configuration compiler for JWeBot" requires-python = ">=3.9" dependencies = [ diff --git a/packages/jwebot-config/src/jwebot_config/__init__.py b/packages/jwebot-config/src/jwebot_config/__init__.py index 5d502ef..d5f6150 100644 --- a/packages/jwebot-config/src/jwebot_config/__init__.py +++ b/packages/jwebot-config/src/jwebot_config/__init__.py @@ -36,4 +36,4 @@ __all__ = [ "write_default_profile", ] -__version__ = "0.6.7a0+1015" +__version__ = "0.6.8a0+1015" diff --git a/packages/jwebot-extension-host/pyproject.toml b/packages/jwebot-extension-host/pyproject.toml index eb8ddc3..86ecb5f 100644 --- a/packages/jwebot-extension-host/pyproject.toml +++ b/packages/jwebot-extension-host/pyproject.toml @@ -4,12 +4,12 @@ build-backend = "setuptools.build_meta" [project] name = "jwebot-extension-host" -version = "0.6.7a0+1015" +version = "0.6.8a0+1015" description = "Single-release, single-invocation process host for JWeBot extensions" requires-python = ">=3.9" dependencies = [ - "jwebot-sdk==0.6.7a0+1015", - "jwebot-kernel==0.6.7a0+1015", + "jwebot-sdk==0.6.8a0+1015", + "jwebot-kernel==0.6.8a0+1015", ] [project.scripts] diff --git a/packages/jwebot-extension-host/src/jwebot_extension_host/__init__.py b/packages/jwebot-extension-host/src/jwebot_extension_host/__init__.py index 6f9cd85..f7bf726 100644 --- a/packages/jwebot-extension-host/src/jwebot_extension_host/__init__.py +++ b/packages/jwebot-extension-host/src/jwebot_extension_host/__init__.py @@ -16,4 +16,4 @@ __all__ = [ "process_request", ] -__version__ = "0.6.7a0+1015" +__version__ = "0.6.8a0+1015" diff --git a/packages/jwebot-kernel/pyproject.toml b/packages/jwebot-kernel/pyproject.toml index 348de3e..d3ec683 100644 --- a/packages/jwebot-kernel/pyproject.toml +++ b/packages/jwebot-kernel/pyproject.toml @@ -4,13 +4,13 @@ build-backend = "setuptools.build_meta" [project] name = "jwebot-kernel" -version = "0.6.7a0+1015" +version = "0.6.8a0+1015" description = "Stable routing and extension authority for JWeBot" requires-python = ">=3.9" dependencies = [ - "jwebot-config==0.6.7a0+1015", - "jwebot-sdk==0.6.7a0+1015", - "jwebot-official-extensions==0.6.7a0+1015", + "jwebot-config==0.6.8a0+1015", + "jwebot-sdk==0.6.8a0+1015", + "jwebot-official-extensions==0.6.8a0+1015", "tomli>=2; python_version < '3.11'", ] diff --git a/packages/jwebot-kernel/src/jwebot_kernel/__init__.py b/packages/jwebot-kernel/src/jwebot_kernel/__init__.py index 6d20ca5..04342e5 100644 --- a/packages/jwebot-kernel/src/jwebot_kernel/__init__.py +++ b/packages/jwebot-kernel/src/jwebot_kernel/__init__.py @@ -107,4 +107,4 @@ __all__ = [ "verify_installed_release_signature", ] -__version__ = "0.6.7a0+1015" +__version__ = "0.6.8a0+1015" diff --git a/packages/jwebot-kernel/src/jwebot_kernel/edge_runtime.py b/packages/jwebot-kernel/src/jwebot_kernel/edge_runtime.py index 517159b..785cac8 100644 --- a/packages/jwebot-kernel/src/jwebot_kernel/edge_runtime.py +++ b/packages/jwebot-kernel/src/jwebot_kernel/edge_runtime.py @@ -3837,6 +3837,29 @@ class Ledger: self._validate_autochess_state(new_state) run_id = str(new_state["run_id"]) season_number = int(new_state["season_number"]) + if ( + stored is not None + and str(stored["run_id"]) != run_id + and str(stored["state"]) in {"registration", "active"} + ): + archived_state = dict(state or {}) + archived_state["status"] = "cancelled" + archived_state["phase"] = "cancelled" + self.connection.execute( + """ + UPDATE autochess_runs + SET state = 'cancelled', + state_json = ?, + updated_at = ? + WHERE group_fingerprint = ? AND run_id = ? + """, + ( + canonical_json(archived_state), + timestamp, + group, + str(stored["run_id"]), + ), + ) existing = self.connection.execute( """ SELECT created_at FROM autochess_runs diff --git a/packages/jwebot-official-extensions/pyproject.toml b/packages/jwebot-official-extensions/pyproject.toml index 6b3edfa..d830bc6 100644 --- a/packages/jwebot-official-extensions/pyproject.toml +++ b/packages/jwebot-official-extensions/pyproject.toml @@ -4,11 +4,11 @@ build-backend = "setuptools.build_meta" [project] name = "jwebot-official-extensions" -version = "0.6.7a0+1015" +version = "0.6.8a0+1015" description = "Reviewed local command adapters and broker implementation for JWeBot" requires-python = ">=3.9" dependencies = [ - "jwebot-sdk==0.6.7a0+1015", + "jwebot-sdk==0.6.8a0+1015", ] [tool.setuptools.packages.find] diff --git a/packages/jwebot-official-extensions/src/jwebot_official_extensions/__init__.py b/packages/jwebot-official-extensions/src/jwebot_official_extensions/__init__.py index a6252aa..2ac3b3a 100644 --- a/packages/jwebot-official-extensions/src/jwebot_official_extensions/__init__.py +++ b/packages/jwebot-official-extensions/src/jwebot_official_extensions/__init__.py @@ -90,4 +90,4 @@ __all__ = [ "render_feature_list", ] -__version__ = "0.6.7a0+1015" +__version__ = "0.6.8a0+1015" diff --git a/packages/jwebot-official-extensions/src/jwebot_official_extensions/cs_autochess.py b/packages/jwebot-official-extensions/src/jwebot_official_extensions/cs_autochess.py index 4cdcfe1..9b3419c 100644 --- a/packages/jwebot-official-extensions/src/jwebot_official_extensions/cs_autochess.py +++ b/packages/jwebot-official-extensions/src/jwebot_official_extensions/cs_autochess.py @@ -2834,7 +2834,16 @@ class CsMajorAutochess: if action == "unknown": return respond(state, self.rules()) if action == "create": - if state is not None and state.get("status") in {"registration", "active"}: + legacy_active = ( + state is not None + and state.get("status") in {"registration", "active"} + and int(state.get("schema_version") or 0) < 3 + ) + if ( + state is not None + and state.get("status") in {"registration", "active"} + and not legacy_active + ): return respond( state, f"{state['event_title']} 已在进行中;发送 /自走棋状态 查看", @@ -2878,6 +2887,8 @@ class CsMajorAutochess: f"{state['event_title']} 开放报名;主持人 {display_name} 已自动加入。" "支持 2~8 人;发送 /自走棋报名,主持人发送 /自走棋开赛。" ) + if legacy_active: + text = "已保留旧版未完成赛事档案,并开启新版赛季。" + text story = _story(state, "registration_open", text, {"host_person_key": person_key}) return respond(state, text, stories=[story]) if state is None: diff --git a/packages/jwebot-sdk/pyproject.toml b/packages/jwebot-sdk/pyproject.toml index 801fb92..75c7c59 100644 --- a/packages/jwebot-sdk/pyproject.toml +++ b/packages/jwebot-sdk/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "jwebot-sdk" -version = "0.6.7a0+1015" +version = "0.6.8a0+1015" description = "Typed, capability-limited extension API for JWeBot plugins" requires-python = ">=3.9" dependencies = [] diff --git a/packages/jwebot-sdk/src/jwebot_sdk/__init__.py b/packages/jwebot-sdk/src/jwebot_sdk/__init__.py index de1fa51..ce6b086 100644 --- a/packages/jwebot-sdk/src/jwebot_sdk/__init__.py +++ b/packages/jwebot-sdk/src/jwebot_sdk/__init__.py @@ -38,4 +38,4 @@ __all__ = [ "SingleGroupContext", ] -__version__ = "0.6.7a0+1015" +__version__ = "0.6.8a0+1015" diff --git a/pyproject.toml b/pyproject.toml index 192d8ac..874d8bb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,5 +1,5 @@ [tool.jwebot] -release = "alpha 0.6.7.1015" +release = "alpha 0.6.8.1015" production_deployments_enabled_by_default = false [tool.jwebot.workspace] diff --git a/tests/test_release_067.py b/tests/test_release_067.py index 5358efb..35ded89 100644 --- a/tests/test_release_067.py +++ b/tests/test_release_067.py @@ -133,6 +133,34 @@ class Release067AutochessTests(unittest.TestCase): self.assertNotIn("克制修正", report) self.assertIsNotNone(re.search(r"\b\d{2}-\d{1,2}\b", report)) + def test_legacy_active_run_does_not_block_new_schema_three_season(self): + self.command("/自走棋开始") + state = self.state() + state["schema_version"] = 2 + state["status"] = "active" + self.engine.ledger.connection.execute( + """ + UPDATE autochess_runs + SET state = 'active', state_json = ? + WHERE group_fingerprint = 'group-a' AND run_id = ? + """, + ( + json.dumps(state, ensure_ascii=False, sort_keys=True), + state["run_id"], + ), + ) + self.engine.ledger.connection.commit() + + response = "\n".join( + self.command("/自走棋开始", event_id="legacy-active-create") + ) + new_state = self.state() + + self.assertIn("旧版未完成赛事", response) + self.assertEqual(new_state["schema_version"], 3) + self.assertEqual(new_state["season_number"], 2) + self.assertEqual(new_state["status"], "registration") + if __name__ == "__main__": unittest.main()