diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 5be0c56..6bc5f53 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,5 +1,5 @@ blank_issues_enabled: false contact_links: - - name: Verified run-feedback template - url: https://github.com/cyou1314/cinethread/blob/main/docs/feedback-template.md - about: Use this format only after a real run. State whether you are independent from the maintainer. + - name: 60-second trial guide + url: https://github.com/cyou1314/cinethread/blob/main/docs/try-it.md + about: Run the public examples before opening a Bug, Feature, or Verified run feedback Issue. diff --git a/.github/ISSUE_TEMPLATE/run-feedback.yml b/.github/ISSUE_TEMPLATE/run-feedback.yml new file mode 100644 index 0000000..cffa59e --- /dev/null +++ b/.github/ISSUE_TEMPLATE/run-feedback.yml @@ -0,0 +1,83 @@ +name: Verified run feedback +description: Share a real result after running CineThread's public verification. +title: "run: " +body: + - type: markdown + attributes: + value: | + Thank you for running CineThread. This form is for an actual result, + whether all checks passed or not. Do not include private media, + credentials, cookies, or local absolute paths. + - type: input + id: version + attributes: + label: Version or commit + description: Run `aigc-director-kit --version` or provide a release tag or commit SHA. + placeholder: v0.2.2 + validations: + required: true + - type: input + id: environment + attributes: + label: Environment + description: OS, Python version, and how you obtained the repository. + placeholder: Windows 11, Python 3.12, downloaded release ZIP + validations: + required: true + - type: textarea + id: command + attributes: + label: Command or launcher used + description: For example, `run_examples.bat` or the exact `verify-examples` command. + render: shell + validations: + required: true + - type: dropdown + id: outcome + attributes: + label: Result + options: + - All public checks passed + - One or more checks failed + - Could not complete the run + validations: + required: true + - type: textarea + id: observations + attributes: + label: Observations or sanitized report excerpt + description: Attach or paste the generated JSON report only after reviewing it for private information. + validations: + required: true + - type: dropdown + id: relationship + attributes: + label: Relationship to the maintainer + options: + - Independent user + - Maintainer + - Maintainer alternate account + - Collaborator + validations: + required: true + - type: dropdown + id: quotation_permission + attributes: + label: Maintenance quotation permission + description: This does not change the public visibility of this Issue. + options: + - May quote a short anonymized summary + - Do not quote this feedback + validations: + required: true + - type: checkboxes + id: safeguards + attributes: + label: Safeguards + options: + - label: I ran the command or launcher above myself. + required: true + - label: I removed credentials, private assets, and local absolute paths. + required: true + - label: I am not presenting a maintainer alternate account as independent adoption. + required: true diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bf1c004..b00c97d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -27,14 +27,15 @@ jobs: python-version: ${{ matrix.python-version }} - name: Install package - run: python -m pip install --no-deps -e . + run: python -m pip install --no-deps . - name: Run unit tests run: python -m unittest discover -s tests -v - name: Validate public examples run: | - python -m aigc_director_kit verify-examples --json + aigc-director-kit --version + aigc-director-kit verify-examples --root . --json python -m aigc_director_kit validate-plan examples/shot_plan.json --json python -m aigc_director_kit validate-plan examples/one_take_previs_case.json --json python -m aigc_director_kit validate-plan examples/film_contract_handoff_case.json --json diff --git a/CHANGELOG.md b/CHANGELOG.md index 4b920e1..45e853f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # CineThread Changelog +## [0.2.2] - 2026-08-12 + +- Added a package version field and safe JSON report export to `verify-examples`, plus `aigc-director-kit --version`. +- Added a structured Verified run feedback Issue Form and a 60-second trial guide for actual user testing. +- Changed CI to test an ordinary non-editable package installation and the installed CLI entrypoint. + ## [0.2.1] - 2026-08-12 - Added `verify-examples`, a cross-platform, privacy-safe one-command verification report for every public example. diff --git a/README.md b/README.md index fc36fde..a90fbf8 100644 --- a/README.md +++ b/README.md @@ -13,16 +13,17 @@ repository folder: ```powershell # Windows terminal (when the Python launcher is installed) -py -3 -m aigc_director_kit verify-examples --json +py -3 -m aigc_director_kit verify-examples --json --output cinethread-verify.json # macOS or Linux -python3 -m aigc_director_kit verify-examples --json +python3 -m aigc_director_kit verify-examples --json --output cinethread-verify.json ``` It checks every public plan, workflow, prompt-pack, QC-boundary, action, and runtime-handoff example, and returns a privacy-safe report with relative paths only. It does not render or generate media. For a real bug report, review the -output first and use the [verified feedback template](docs/feedback-template.md). +output first and use the [60-second trial guide](docs/try-it.md) or the +[Verified run feedback](https://github.com/cyou1314/cinethread/issues/new/choose) form. Windows users who do not know which Python command to use can instead double-click [`run_examples.bat`](run_examples.bat); it detects common Python installations. @@ -77,10 +78,10 @@ observed evidence from an available artifact. ## Contribute and report a real run Use the [contribution guide](CONTRIBUTING.md), [Code of Conduct](CODE_OF_CONDUCT.md), -and [verified feedback template](docs/feedback-template.md). Bug reports and -feature requests use structured GitHub forms so that environment, commands, -and evidence boundaries are clear. A maintainer's alternate account may report -a real bug, but it is not independent adoption evidence. +and [verified feedback template](docs/feedback-template.md). Bug reports, +feature requests, and verified run feedback use structured GitHub forms so that +environment, commands, and evidence boundaries are clear. A maintainer's +alternate account may report a real bug, but it is not independent adoption evidence. [![CI](https://github.com/cyou1314/cinethread/actions/workflows/ci.yml/badge.svg)](https://github.com/cyou1314/cinethread/actions/workflows/ci.yml) @@ -114,7 +115,7 @@ Windows 用户也可以直接双击 [`run_examples.bat`](run_examples.bat) 查 命令行方式: ```powershell -python -m aigc_director_kit verify-examples --json +python -m aigc_director_kit verify-examples --json --output cinethread-verify.json python -m aigc_director_kit validate-plan examples/shot_plan.json python -m aigc_director_kit list-actions --library examples/action_library.json --query 跑 python -m aigc_director_kit compile-action ` @@ -127,6 +128,8 @@ python -m aigc_director_kit compile-action ` ```powershell python -m pip install -e . +aigc-director-kit --version +aigc-director-kit verify-examples --root . --output cinethread-verify.json aigc-director-kit validate-plan examples/shot_plan.json ``` diff --git a/docs/feedback-template.md b/docs/feedback-template.md index 920b98e..a13f51c 100644 --- a/docs/feedback-template.md +++ b/docs/feedback-template.md @@ -2,13 +2,13 @@ 只填写实际运行过的结果,不要为了活动申请制造反馈、Stars、Issue 或 PR。 -建议先在克隆或解压后的仓库目录运行: +最快方式是先在克隆或解压后的仓库目录运行: ```powershell -python -m aigc_director_kit verify-examples --json +python -m aigc_director_kit verify-examples --json --output cinethread-verify.json ``` -它只输出相对路径和通用环境信息;在公开前仍请自行检查输出,删除不应公开的内容。 +它会写入带版本号的报告,只输出相对路径和通用环境信息;在公开前仍请自行检查输出,删除不应公开的内容。随后在仓库的 **Issues → New issue** 中选择 **Verified run feedback**,或参考 [`docs/try-it.md`](try-it.md) 的完整步骤。 ```text 版本/commit: diff --git a/docs/try-it.md b/docs/try-it.md new file mode 100644 index 0000000..35808c9 --- /dev/null +++ b/docs/try-it.md @@ -0,0 +1,34 @@ +# 60 秒试用 CineThread + +这是一套公开 JSON 合同和确定性干跑校验工具。它不会联网、不会调用付费模型、不会生成或渲染视频,也不会检查私人素材。 + +## 1. 获取仓库 + +在 GitHub 仓库页选择 **Code → Download ZIP**,解压后进入 `cinethread` 文件夹;也可以使用你熟悉的 Git 克隆方式。 + +## 2. 运行公开验证 + +Windows 用户可直接双击 `run_examples.bat`。脚本会检查常见 Python 安装位置,并运行全部公开示例。 + +在终端中,也可以运行: + +```powershell +# Windows(已安装 Python Launcher) +py -3 -m aigc_director_kit verify-examples --json --output cinethread-verify.json + +# macOS / Linux +python3 -m aigc_director_kit verify-examples --json --output cinethread-verify.json +``` + +检查成功时,报告会显示 `valid: true`,并显示 11 项公开检查全部通过。`cinethread-verify.json` 只包含相对路径、版本和通用运行环境;公开前仍请自行检查内容。 + +## 3. 留下真实结果 + +在仓库的 **Issues → New issue** 中选择 **Verified run feedback**: + +- 全部通过也可以提交,它仍是有价值的真实运行记录; +- 失败或无法完成时,填写实际错误; +- 选择你与维护者的真实关系;维护者自己的其他账号不算独立采用; +- 不要附私人素材、账号、Cookie、密钥或本机绝对路径。 + +如果发现明确的程序错误,也可以使用 **Bug report** 表单。 diff --git a/pyproject.toml b/pyproject.toml index 8226875..4766c9e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "aigc-director-kit" -version = "0.2.1" +version = "0.2.2" description = "CineThread: deterministic creative handoffs for AI animation, storyboards, and previs." readme = "README.md" requires-python = ">=3.10" diff --git a/src/aigc_director_kit/__init__.py b/src/aigc_director_kit/__init__.py index a989fc5..08356fe 100644 --- a/src/aigc_director_kit/__init__.py +++ b/src/aigc_director_kit/__init__.py @@ -1,5 +1,6 @@ """CineThread public Python API.""" +from ._version import __version__ from .actions import compile_action_request, list_actions, load_action_library from .contract import ValidationResult, load_plan, validate_plan, validate_plan_file from .prompt import validate_prompt_pack, validate_prompt_pack_file @@ -10,6 +11,7 @@ __all__ = [ "ValidationResult", + "__version__", "compile_action_request", "find_project_root", "list_actions", @@ -27,5 +29,3 @@ "validate_workflow", "validate_workflow_file", ] - -__version__ = "0.2.1" diff --git a/src/aigc_director_kit/_version.py b/src/aigc_director_kit/_version.py new file mode 100644 index 0000000..8da975f --- /dev/null +++ b/src/aigc_director_kit/_version.py @@ -0,0 +1,3 @@ +"""Single source of truth for the public package version.""" + +__version__ = "0.2.2" diff --git a/src/aigc_director_kit/cli.py b/src/aigc_director_kit/cli.py index faa9fd0..3f4e647 100644 --- a/src/aigc_director_kit/cli.py +++ b/src/aigc_director_kit/cli.py @@ -8,6 +8,7 @@ from pathlib import Path from typing import Any +from ._version import __version__ from .actions import compile_action_request, list_actions, load_action_library from .contract import validate_plan_file from .prompt import validate_prompt_pack_file @@ -23,6 +24,7 @@ def _dump(value: Any) -> None: def _build_parser() -> argparse.ArgumentParser: parser = argparse.ArgumentParser(prog="aigc-director-kit") + parser.add_argument("--version", action="version", version=f"%(prog)s {__version__}") subparsers = parser.add_subparsers(dest="command", required=True) validate = subparsers.add_parser("validate-plan", help="Validate a shot-plan JSON file.") @@ -71,6 +73,11 @@ def _build_parser() -> argparse.ArgumentParser: type=Path, help="CineThread source checkout; defaults to the current directory or a parent.", ) + verify.add_argument( + "--output", + type=Path, + help="Write the report to a new JSON file; existing files are never overwritten.", + ) verify.add_argument("--json", action="store_true", dest="as_json") search = subparsers.add_parser("list-actions", help="Search an action catalog.") @@ -90,7 +97,7 @@ def _build_parser() -> argparse.ArgumentParser: def _write_new_json(path: Path, value: Any) -> None: target = path.resolve() if target.exists(): - raise FileExistsError(f"Refusing to overwrite existing output: {target}") + raise FileExistsError("Refusing to overwrite existing output. Choose a new output path.") target.parent.mkdir(parents=True, exist_ok=True) target.write_text(json.dumps(value, ensure_ascii=False, indent=2) + "\n", encoding="utf-8") @@ -177,9 +184,12 @@ def main(argv: list[str] | None = None) -> int: if args.command == "verify-examples": report = run_public_example_verification(args.root) + if args.output: + _write_new_json(args.output, report) if args.as_json: _dump(report) else: + print(f"version: {report['version']}") print(f"valid: {'yes' if report['valid'] else 'no'}") print(f"checks: {report['summary']['check_count']}") print(f"passed_checks: {report['summary']['passed_check_count']}") @@ -190,6 +200,8 @@ def main(argv: list[str] | None = None) -> int: for warning in check["warnings"]: print(f"warning: {warning}") print(f"boundary: {report['boundary']}") + if args.output: + print("report_written: yes") return 0 if report["valid"] else 2 if args.command == "list-actions": diff --git a/src/aigc_director_kit/verification.py b/src/aigc_director_kit/verification.py index c593469..ba7acc7 100644 --- a/src/aigc_director_kit/verification.py +++ b/src/aigc_director_kit/verification.py @@ -9,6 +9,7 @@ from pathlib import Path from typing import Any +from ._version import __version__ from .actions import compile_action_request, list_actions, load_action_library from .contract import ValidationResult, validate_plan_file from .prompt import validate_prompt_pack_file @@ -235,6 +236,7 @@ def verify_runtime_handoff() -> dict[str, Any]: return { "tool": "CineThread", "package": "aigc-director-kit", + "version": __version__, "environment": { "python": platform.python_version(), "implementation": platform.python_implementation(), diff --git a/tests/test_cli.py b/tests/test_cli.py index 7f59ac1..a07f2f1 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -1,8 +1,11 @@ +import contextlib +import io import json import tempfile import unittest from pathlib import Path +from aigc_director_kit import __version__ from aigc_director_kit.cli import main @@ -49,6 +52,58 @@ def test_verify_examples_json_mode(self) -> None: def test_verify_examples_rejects_an_invalid_explicit_root(self) -> None: self.assertEqual(main(["verify-examples", "--root", str(ROOT / "missing")]), 1) + def test_cli_reports_the_package_version(self) -> None: + output = io.StringIO() + with contextlib.redirect_stdout(output): + with self.assertRaises(SystemExit) as result: + main(["--version"]) + + self.assertEqual(result.exception.code, 0) + self.assertEqual(output.getvalue(), f"aigc-director-kit {__version__}\n") + + def test_verify_examples_writes_a_versioned_report_without_local_paths(self) -> None: + with tempfile.TemporaryDirectory() as temp_dir: + output_path = Path(temp_dir) / "cinethread-verify.json" + with contextlib.redirect_stdout(io.StringIO()): + self.assertEqual( + main( + [ + "verify-examples", + "--root", + str(ROOT), + "--output", + str(output_path), + ] + ), + 0, + ) + + report = json.loads(output_path.read_text(encoding="utf-8")) + self.assertEqual(report["version"], __version__) + self.assertNotIn(str(ROOT), json.dumps(report, ensure_ascii=False)) + + def test_verify_examples_refuses_to_overwrite_a_report(self) -> None: + with tempfile.TemporaryDirectory() as temp_dir: + output_path = Path(temp_dir) / "cinethread-verify.json" + output_path.write_text("{}", encoding="utf-8") + errors = io.StringIO() + with contextlib.redirect_stderr(errors): + self.assertEqual( + main( + [ + "verify-examples", + "--root", + str(ROOT), + "--output", + str(output_path), + ] + ), + 1, + ) + + self.assertIn("Refusing to overwrite existing output", errors.getvalue()) + self.assertNotIn(str(output_path), errors.getvalue()) + if __name__ == "__main__": unittest.main() diff --git a/tests/test_verification.py b/tests/test_verification.py index 5592e6e..ec726b4 100644 --- a/tests/test_verification.py +++ b/tests/test_verification.py @@ -3,6 +3,7 @@ import unittest from pathlib import Path +from aigc_director_kit import __version__ from aigc_director_kit.verification import find_project_root, run_public_example_verification @@ -14,6 +15,7 @@ def test_verifies_all_public_examples_without_local_paths(self) -> None: report = run_public_example_verification(ROOT) self.assertTrue(report["valid"]) + self.assertEqual(report["version"], __version__) self.assertEqual(report["summary"], {"check_count": 11, "passed_check_count": 11}) self.assertNotIn(str(ROOT), json.dumps(report, ensure_ascii=False)) self.assertTrue(all(check["valid"] for check in report["checks"])) diff --git a/tests/test_version.py b/tests/test_version.py index cd8abd9..f6e113e 100644 --- a/tests/test_version.py +++ b/tests/test_version.py @@ -13,7 +13,7 @@ def test_runtime_and_project_versions_match(self) -> None: project_text = (ROOT / "pyproject.toml").read_text(encoding="utf-8") project_version = re.search(r'^version\s*=\s*"([^"]+)"\s*$', project_text, re.MULTILINE) self.assertIsNotNone(project_version) - self.assertEqual(__version__, "0.2.1") + self.assertEqual(__version__, "0.2.2") self.assertEqual(__version__, project_version.group(1))