修:draw-ui / oil-motion 原被当子模块指针收录 ⇒ 改为正常文件入库(两份内容原先对别人是空的)

一、问题(本轮实测)
`draw-ui` 与 `oil-motion` 目录里**各自带一个内嵌 `.git`** ⇒ 上一次提交把它们记成了 **gitlink(子模块指针)**
⇒ 仓库里只存了一个不属于任何远端的 commit id,**别人克隆下来这两份是空的** ✗(`git status` 显示 ` m draw-ui` / ` m oil-motion` = 子模块内容有改动)。

二、处置(可回退)
· 把两处的 `.git` **挪走**(⛔ 不是删除)⇒ `归档/内嵌git-20261008/{draw-ui,oil-motion}.git`;
· `git rm --cached` 掉那两个 gitlink,再 `git add` 两个目录 ⇒ **按正常文件入库**(内容才真的进仓库)。

三、副作用(如实记)
挪走 `.git` 后,这两个技能**不能再原地 `git pull` 取上游更新**(要更新得重新拉一份覆盖);
如需恢复其本地仓库,把 `归档/内嵌git-20261008/` 里的 `.git` 挪回原处即可。
This commit is contained in:
admin committed 2026-10-08 22:29:52 +08:00
1 parent e03465c398
commit 237a09a5b0
161 files changed
+19429 -2

No files matched your search

+252
View File
@@ -0,0 +1,252 @@
from __future__ import annotations
import importlib.util
import json
import shutil
import subprocess
import sys
import tempfile
import unittest
from argparse import Namespace
from pathlib import Path
from PIL import Image
SCRIPT_DIR = Path(__file__).resolve().parents[1] / "scripts"
sys.path.insert(0, str(SCRIPT_DIR))
def load(name: str):
spec = importlib.util.spec_from_file_location(name, SCRIPT_DIR / f"{name}.py")
assert spec and spec.loader
module = importlib.util.module_from_spec(spec)
sys.modules[spec.name] = module
spec.loader.exec_module(module)
return module
GATE = load("production_gate")
VIDEO = load("video_job")
FFMPEG = shutil.which("ffmpeg")
def write(path: Path, value: bytes) -> Path:
path.write_bytes(value)
return path
def make_color_video(path: Path, color: str) -> None:
subprocess.run(
[
FFMPEG or "ffmpeg",
"-hide_banner",
"-loglevel",
"error",
"-y",
"-f",
"lavfi",
"-i",
f"color={color}:size=64x64:rate=12:duration=1",
"-pix_fmt",
"yuv420p",
str(path),
],
check=True,
)
class ProductionGateTests(unittest.TestCase):
def approval(self, root: Path, continuity: str = "chain") -> Path:
files = {
"contract": write(
root / "contract.yaml",
f"subject_count: 1\nclip_continuity: {continuity}\n".encode(),
),
"first_frame": write(root / "first.png", b"first"),
"last_frame": write(root / "last.png", b"last"),
"video": write(root / "pilot.mp4", b"video"),
"page_evidence": write(root / "page.png", b"page"),
}
output = root / "approval.json"
GATE.create_pilot_approval(
Namespace(
**files,
identity_bible=None,
reviewer="visual-review",
notes="passed",
decision="pass",
output=output,
force=False,
)
)
return output
def test_pilot_approval_detects_changed_artifact(self) -> None:
with tempfile.TemporaryDirectory() as directory:
root = Path(directory)
approval = self.approval(root)
GATE.validate_pilot_approval(approval)
# Updating pageEvidence does not invalidate pilot approval as long as file exists
(root / "page.png").write_bytes(b"updated screenshot")
self.assertTrue(GATE.validate_pilot_approval(approval)["passed"])
# Changing core generation artifact invalidates pilot approval
(root / "first.png").write_bytes(b"changed")
with self.assertRaisesRegex(ValueError, "批准失效"):
GATE.validate_pilot_approval(approval)
def test_contract_continuity_requires_unique_top_level_value(self) -> None:
invalid_contracts = {
"nested": "metadata:\n clip_continuity: independent\n",
"duplicate": (
"clip_continuity: chain\n"
"clip_continuity: independent\n"
),
"string": 'notes: "clip_continuity: independent"\n',
"comment": "# clip_continuity: independent\nsubject_count: 1\n",
"missing": "subject_count: 1\n",
}
with tempfile.TemporaryDirectory() as directory:
root = Path(directory)
for name, content in invalid_contracts.items():
with self.subTest(name=name):
path = root / f"{name}.yaml"
path.write_text(content, encoding="utf-8")
with self.assertRaises(ValueError):
GATE.contract_continuity_mode(path)
valid = root / "valid.yaml"
valid.write_text(
"subject_count: 1\nclip_continuity: chain # required\n",
encoding="utf-8",
)
self.assertEqual(GATE.contract_continuity_mode(valid), "chain")
def test_frame_chain_records_exact_hash_match(self) -> None:
with tempfile.TemporaryDirectory() as directory:
root = Path(directory)
previous = write(root / "tail.png", b"same-frame")
current = write(root / "first.png", b"same-frame")
manifest = root / "frame-chain.json"
link = GATE.verify_frame_chain(previous, current, 2, manifest)
self.assertTrue(link["exactSha256Match"])
payload = json.loads(manifest.read_text(encoding="utf-8"))
self.assertEqual(payload["links"][0]["segmentIndex"], 2)
def test_frame_chain_rejects_modified_next_first(self) -> None:
with tempfile.TemporaryDirectory() as directory:
root = Path(directory)
previous = write(root / "tail.png", b"tail")
current = write(root / "first.png", b"not-tail")
with self.assertRaisesRegex(ValueError, "连续帧链断裂"):
GATE.verify_frame_chain(previous, current, 2)
def test_frame_similarity_rejects_visible_change(self) -> None:
with tempfile.TemporaryDirectory() as directory:
root = Path(directory)
first = root / "first.png"
second = root / "second.png"
Image.new("RGB", (32, 32), (255, 0, 0)).save(first)
Image.new("RGB", (32, 32), (0, 0, 255)).save(second)
metrics = GATE.frame_similarity(first, second)
self.assertLess(metrics["ssim"], 0.97)
self.assertGreater(metrics["normalizedMae"], 0.04)
@unittest.skipUnless(FFMPEG, "需要 ffmpeg 才能验证成片接缝")
def test_output_chain_checks_decoded_video_edges(self) -> None:
with tempfile.TemporaryDirectory() as directory:
root = Path(directory)
previous = root / "previous.mp4"
current = root / "current.mp4"
make_color_video(previous, "red")
make_color_video(current, "red")
result = GATE.verify_output_chain(
previous,
current,
2,
root / "frame-chain.json",
root / "evidence",
)
self.assertTrue(result["passed"])
self.assertIn("ssim", result["metrics"])
def test_video_production_requires_pilot_approval(self) -> None:
args = Namespace(
stage="production",
segment_index=2,
pilot_approval=None,
continuity_mode="independent",
previous_tail=None,
first_frame=None,
frame_chain_manifest=None,
)
with self.assertRaisesRegex(ValueError, "pilot-approval"):
VIDEO.validate_production_gate(args)
def test_video_chain_requires_exact_previous_tail(self) -> None:
with tempfile.TemporaryDirectory() as directory:
root = Path(directory)
approval = self.approval(root)
previous = write(root / "tail.png", b"tail")
current = write(root / "next.png", b"different")
args = Namespace(
stage="production",
segment_index=2,
pilot_approval=approval,
continuity_mode="chain",
previous_tail=previous,
first_frame=current,
frame_chain_manifest=root / "frame-chain.json",
)
with self.assertRaisesRegex(ValueError, "连续帧链断裂"):
VIDEO.validate_production_gate(args)
def test_continuous_contract_rejects_independent_bypass(self) -> None:
with tempfile.TemporaryDirectory() as directory:
root = Path(directory)
approval = self.approval(root, "chain")
args = Namespace(
stage="production",
segment_index=2,
pilot_approval=approval,
continuity_mode="independent",
previous_tail=None,
first_frame=None,
frame_chain_manifest=None,
)
with self.assertRaisesRegex(ValueError, "合同要求 chain"):
VIDEO.validate_production_gate(args)
def test_independent_contract_allows_independent_production(self) -> None:
with tempfile.TemporaryDirectory() as directory:
root = Path(directory)
approval = self.approval(root, "independent")
args = Namespace(
stage="production",
segment_index=2,
pilot_approval=approval,
continuity_mode="independent",
previous_tail=None,
first_frame=None,
frame_chain_manifest=None,
)
result = VIDEO.validate_production_gate(args)
self.assertEqual(result["continuityMode"], "independent")
if __name__ == "__main__":
unittest.main()