From c004a7d1f39c80c549d4cb7ae732d4b7297d34dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BC=98=E9=9B=85=E3=81=AE=E5=92=B8=E9=B1=BC?= <3129538298@qq.com> Date: Wed, 26 Aug 2026 16:33:12 +0800 Subject: [PATCH 01/12] chore: bump package version to 0.2.2 --- src/shotbible/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shotbible/__init__.py b/src/shotbible/__init__.py index 960c33f..4f423a4 100644 --- a/src/shotbible/__init__.py +++ b/src/shotbible/__init__.py @@ -1,3 +1,3 @@ """shotbible — local continuity bible for AI image/video productions.""" -__version__ = "0.2.1" +__version__ = "0.2.2" From 39a5ca96f50a1f9515d76938705c444ed546cd61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BC=98=E9=9B=85=E3=81=AE=E5=92=B8=E9=B1=BC?= <3129538298@qq.com> Date: Wed, 26 Aug 2026 16:34:00 +0800 Subject: [PATCH 02/12] test: compileall sources and tests in pytest --- tests/test_compileall.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 tests/test_compileall.py diff --git a/tests/test_compileall.py b/tests/test_compileall.py new file mode 100644 index 0000000..5c06269 --- /dev/null +++ b/tests/test_compileall.py @@ -0,0 +1,12 @@ +"""CI already runs pytest; this keeps a syntax check without editing workflows.""" +from __future__ import annotations + +import compileall +from pathlib import Path + +ROOT = Path(__file__).resolve().parents[1] + + +def test_sources_and_tests_compile() -> None: + assert compileall.compile_dir(str(ROOT / "src"), quiet=1, force=True) + assert compileall.compile_dir(str(ROOT / "tests"), quiet=1, force=True) From b615cac64b1821137c8c378924e36454e1d6f32f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BC=98=E9=9B=85=E3=81=AE=E5=92=B8=E9=B1=BC?= <3129538298@qq.com> Date: Wed, 26 Aug 2026 16:34:16 +0800 Subject: [PATCH 03/12] chore: set package version 0.2.2 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index fc5e971..c0b65f7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "shotbible" -version = "0.2.1" +version = "0.2.2" description = "Local continuity bible for AI image/video productions — lock characters, scenes, and compile consistent prompts." readme = "README.md" license = { text = "MIT" } From 2e10968354c5a798d56d5e867275a13224942336 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BC=98=E9=9B=85=E3=81=AE=E5=92=B8=E9=B1=BC?= <3129538298@qq.com> Date: Wed, 26 Aug 2026 16:38:01 +0800 Subject: [PATCH 04/12] docs: record PyYAML honesty and take-list validation --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 19868fe..09cdb92 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## 0.2.2 — 2026-08-26 + +- README 改成如实写运行时依赖 PyYAML(不再写「零第三方依赖」) +- 损坏的 `takes:` 列表项(字符串 / 空值)现在报错,不再被静默丢掉 +- 测试增加 `compileall` 语法检查 + ## 0.2.1 — 2026-08-17 - `check` warns when a character `look` is too short to lock identity From 964653a5d7f4709baf53514f88457d73b5246a7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BC=98=E9=9B=85=E3=81=AE=E5=92=B8=E9=B1=BC?= <3129538298@qq.com> Date: Wed, 26 Aug 2026 16:38:56 +0800 Subject: [PATCH 05/12] fix: import Character and Scene used by store helpers --- src/shotbible/store.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/shotbible/store.py b/src/shotbible/store.py index 80f0d6d..f45b540 100644 --- a/src/shotbible/store.py +++ b/src/shotbible/store.py @@ -5,7 +5,7 @@ import yaml -from .models import Bible, ParseError, Take +from .models import Bible, Character, ParseError, Scene, Take BIBLE_NAME = "bible.yaml" @@ -111,7 +111,7 @@ def copy_asset(root: Path, src: Path, kind: str, bucket: str) -> str: def _safe_id(value: str) -> str: cleaned = "".join(ch if ch.isalnum() or ch in "-_" else "-" for ch in value.strip()) - cleaned = cleaned.strip("-_") or "item" + cleaned = cleaned.strip("-") or "item" return cleaned From 45e322c47f7a9f2dd81c58b1fa75aa525038e8cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BC=98=E9=9B=85=E3=81=AE=E5=92=B8=E9=B1=BC?= <3129538298@qq.com> Date: Wed, 26 Aug 2026 16:39:21 +0800 Subject: [PATCH 06/12] fix: keep underscore stripping in store id sanitizer --- src/shotbible/store.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shotbible/store.py b/src/shotbible/store.py index f45b540..32d6ac0 100644 --- a/src/shotbible/store.py +++ b/src/shotbible/store.py @@ -111,7 +111,7 @@ def copy_asset(root: Path, src: Path, kind: str, bucket: str) -> str: def _safe_id(value: str) -> str: cleaned = "".join(ch if ch.isalnum() or ch in "-_" else "-" for ch in value.strip()) - cleaned = cleaned.strip("-") or "item" + cleaned = cleaned.strip("-_") or "item" return cleaned From 767bbec2b05536f706a26afb85c446c2e49bcd5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BC=98=E9=9B=85=E3=81=AE=E5=92=B8=E9=B1=BC?= <3129538298@qq.com> Date: Wed, 26 Aug 2026 16:39:57 +0800 Subject: [PATCH 07/12] docs: document PyYAML as the runtime dependency --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index e86a5aa..ae8f5d2 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ Paste that file into your image/video model. The compiler never invents wardrobe ## Install -Python 3.10+, zero third-party dependencies. Not on PyPI yet: +Python 3.10+. One runtime dependency: **PyYAML** (for `bible.yaml`). Not on PyPI yet: ```bash pip install git+https://github.com/hc-ui/shotbible.git @@ -53,7 +53,7 @@ shotbible check `prompt -o` writes `takes/s01.prompt.txt`. Paste that file into your video model. New projects default to **9:16**. -A ready-made 9:16 campus-night bible lives in [`examples/campus-night/bible.yaml`](examples/campus-night/bible.yaml). +A ready-made 9:16 campus-night bible lives in [`examples/campus-night`](examples/campus-night/bible.yaml). ## Commands From 22d88c8428c94855d2b68fc741d1d762f0e4c733 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BC=98=E9=9B=85=E3=81=AE=E5=92=B8=E9=B1=BC?= <3129538298@qq.com> Date: Wed, 26 Aug 2026 16:39:58 +0800 Subject: [PATCH 08/12] fix: reject non-mapping take list items instead of dropping them --- src/shotbible/models.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/shotbible/models.py b/src/shotbible/models.py index 2130305..59b264c 100644 --- a/src/shotbible/models.py +++ b/src/shotbible/models.py @@ -150,6 +150,17 @@ def to_dict(self) -> dict[str, Any]: return payload +def _parse_takes(takes_raw: Any) -> list[Take]: + takes: list[Take] = [] + for index, item in enumerate(takes_raw or []): + if not isinstance(item, dict): + raise ParseError( + f"takes[{index}] must be a mapping, got {type(item).__name__}" + ) + takes.append(Take.from_dict(item)) + return takes + + @dataclass class Bible: title: str = "untitled" @@ -191,7 +202,7 @@ def from_dict(cls, data: dict[str, Any] | None) -> "Bible": str(sid): Scene.from_dict(str(sid), s if isinstance(s, dict) else {}) for sid, s in scenes_raw.items() }, - takes=[Take.from_dict(t) for t in takes_raw if isinstance(t, dict)], + takes=_parse_takes(takes_raw), version=version, ) From 4011d707d704a35336060f0f026591e64244f4cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BC=98=E9=9B=85=E3=81=AE=E5=92=B8=E9=B1=BC?= <3129538298@qq.com> Date: Wed, 26 Aug 2026 16:40:00 +0800 Subject: [PATCH 09/12] test: reject bible.yaml takes that are not mappings --- tests/test_store.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/test_store.py b/tests/test_store.py index 7230971..b1ea670 100644 --- a/tests/test_store.py +++ b/tests/test_store.py @@ -104,6 +104,16 @@ def test_load_non_mapping_raises(tmp_path: Path) -> None: load(root) +def test_load_rejects_non_mapping_take(tmp_path: Path) -> None: + root = init_project(tmp_path / "bad-take") + (root / "bible.yaml").write_text( + "version: 1\ntitle: x\ncharacters: {}\nscenes: {}\ntakes:\n - just-a-string\n", + encoding="utf-8", + ) + with pytest.raises(StoreError, match=r"takes\[0\]"): + load(root) + + def test_copy_ref_writes_under_refs(tmp_path: Path) -> None: root = init_project(tmp_path / "refs-proj") src = write_dummy_png(tmp_path / "face.png") From a761d2dc13004debb11baf24fea8337451adc147 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BC=98=E9=9B=85=E3=81=AE=E5=92=B8=E9=B1=BC?= <3129538298@qq.com> Date: Wed, 26 Aug 2026 16:40:36 +0800 Subject: [PATCH 10/12] docs: document PyYAML runtime dependency in Chinese README --- README.zh-CN.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.zh-CN.md b/README.zh-CN.md index da8ae19..050dab3 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -24,7 +24,7 @@ Beat: 阿梅坐在最后一排,打开笔记本,屏幕亮起,她没有看 ## 安装 -Python 3.10+,无第三方依赖。尚未上 PyPI: +Python 3.10+。运行时只依赖 **PyYAML**(读写 `bible.yaml`)。尚未上 PyPI: ```bash pip install git+https://github.com/hc-ui/shotbible.git @@ -53,7 +53,7 @@ shotbible check `prompt -o` 会写出 `takes/s01.prompt.txt`,把文件内容贴进视频模型即可。新建项目默认 **9:16**。 -现成的 9:16 校园夜戏圣经见 [`examples/campus-night/bible.yaml`](examples/campus-night/bible.yaml)。 +现成的 9:16 校园夜戏圣经见 [`examples/campus-night`](examples/campus-night/bible.yaml)。 ## 命令 From ffb8ae42ec1e6b7fe9a2a681db968edeaaef3a9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BC=98=E9=9B=85=E3=81=AE=E5=92=B8=E9=B1=BC?= <3129538298@qq.com> Date: Wed, 26 Aug 2026 16:41:18 +0800 Subject: [PATCH 11/12] docs: keep example bible.yaml link text exact --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ae8f5d2..2c82ba4 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,7 @@ shotbible check `prompt -o` writes `takes/s01.prompt.txt`. Paste that file into your video model. New projects default to **9:16**. -A ready-made 9:16 campus-night bible lives in [`examples/campus-night`](examples/campus-night/bible.yaml). +A ready-made 9:16 campus-night bible lives in [`examples/campus-night/bible.yaml`](examples/campus-night/bible.yaml). ## Commands From 301684d14ee2cb70cd09c2b122e15686a73dcd3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BC=98=E9=9B=85=E3=81=AE=E5=92=B8=E9=B1=BC?= <3129538298@qq.com> Date: Wed, 26 Aug 2026 16:41:20 +0800 Subject: [PATCH 12/12] docs: keep Chinese example bible.yaml link text exact --- README.zh-CN.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.zh-CN.md b/README.zh-CN.md index 050dab3..8a8e816 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -53,7 +53,7 @@ shotbible check `prompt -o` 会写出 `takes/s01.prompt.txt`,把文件内容贴进视频模型即可。新建项目默认 **9:16**。 -现成的 9:16 校园夜戏圣经见 [`examples/campus-night`](examples/campus-night/bible.yaml)。 +现成的 9:16 校园夜戏圣经见 [`examples/campus-night/bible.yaml`](examples/campus-night/bible.yaml)。 ## 命令