mirror of
https://github.com/affaan-m/ECC.git
synced 2026-09-18 15:50:25 +02:00
* feat: bundle standalone taste distillation and application workflows * docs: fix imported taste skill markdown lint * docs: align Turkish agent catalog with taste skills * refactor: make ECC the canonical reusable video engine * fix: preserve video duration when applying image overlays * fix: preserve background colors in image compositing * fix: report best-effort duration targets and shortfalls * feat: ship verified Fusion presets with compatibility provenance * feat(tasteforge): preserve native edits in application bundles * feat(tasteforge): compile local preservation without hosted input * fix: update js-yaml to patched 4.3.2 * test: report bounded Stop wrapper failure diagnostics * fix(tasteforge): fail closed on unsafe output names, missing overlays and cadence - cli: default report and spec paths are derived from pack name and profile genre; require the manifest's name pattern before using either as a filename part so a traversal string cannot write outside cwd/out. - apply_local: a pack without cadence.json, or with no measured shots and no explicit mean_shot, raises instead of silently planning 1.0s shots and reporting a measured cadence. - legacy apply: a missing overlay aborts before any paid upload; forge() would have rejected it after every take was generated. - requirements-live: pin fal-client>=0.13.0, the first release whose subscribe() accepts client_timeout. Addresses the five P1 findings from the independent review of #3033. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015fxHRsydPqEcYngGbqkgt1 --------- Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
398 lines
19 KiB
Python
398 lines
19 KiB
Python
"""Offline, hash-bound insert proposals that preserve a native timeline.
|
|
|
|
This validates local bytes and supplied metadata; it neither probes media nor
|
|
authenticates historical provider claims or human approval. Revalidate before
|
|
use. It never executes an insert, exports a timeline, or contacts a provider.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import copy
|
|
import hashlib
|
|
import json
|
|
import math
|
|
import os
|
|
import re
|
|
import stat
|
|
from fractions import Fraction
|
|
from pathlib import Path
|
|
from typing import Any
|
|
from urllib.parse import urlsplit
|
|
|
|
|
|
_REQUIRED = {"baseline", "source", "audio", "protected_intervals"}
|
|
_OPTIONAL = {"candidates", "inserts", "historical_receipts"}
|
|
_MAX_JSON = 8 * 1024 * 1024
|
|
|
|
|
|
def _canonical(value: Any) -> bytes:
|
|
try:
|
|
return json.dumps(value, sort_keys=True, separators=(",", ":"), allow_nan=False).encode()
|
|
except (TypeError, ValueError, RecursionError) as exc:
|
|
raise ValueError("bundle values must be finite JSON data") from exc
|
|
|
|
|
|
def _digest(value: Any) -> str:
|
|
return hashlib.sha256(_canonical(value)).hexdigest()
|
|
|
|
|
|
def _object(value: Any, required: set[str], optional: set[str] | None = None) -> dict:
|
|
if not isinstance(value, dict) or not required <= value.keys():
|
|
raise ValueError("missing required bundle fields")
|
|
if value.keys() - required - (optional or set()):
|
|
raise ValueError("unknown bundle fields")
|
|
return value
|
|
|
|
|
|
def _text(value: Any) -> str:
|
|
if not isinstance(value, str) or not value.strip():
|
|
raise ValueError("nonempty text required")
|
|
return value
|
|
|
|
|
|
def _integer(value: Any, minimum: int = 0) -> int:
|
|
if type(value) is not int or value < minimum:
|
|
raise ValueError("frame/count must be an exact integer in range")
|
|
return value
|
|
|
|
|
|
def _rate(value: Any) -> tuple[int, int]:
|
|
_object(value, {"numerator", "denominator"})
|
|
n, d = (_integer(value[k], 1) for k in ("numerator", "denominator"))
|
|
if math.gcd(n, d) != 1:
|
|
raise ValueError("fps must be a reduced positive rational")
|
|
return n, d
|
|
|
|
|
|
def _range(value: Any, bounds: list[int] | None = None) -> list[int]:
|
|
if not isinstance(value, list) or len(value) != 2:
|
|
raise ValueError("range must contain two frame integers")
|
|
start, end = (_integer(v) for v in value)
|
|
if start >= end or (bounds is not None and (start < bounds[0] or end > bounds[1])):
|
|
raise ValueError("frame range is empty or outside its bounds")
|
|
return value
|
|
|
|
|
|
def _overlap(a: list[int], b: list[int]) -> bool:
|
|
return a[0] < b[1] and b[0] < a[1]
|
|
|
|
|
|
def _sha(value: Any) -> str:
|
|
if not isinstance(value, str) or not re.fullmatch(r"[a-f0-9]{64}", value):
|
|
raise ValueError("resolved SHA-256 required")
|
|
return value
|
|
|
|
|
|
def _identity(info: os.stat_result) -> tuple:
|
|
return (info.st_dev, info.st_ino, info.st_size, info.st_mtime_ns, info.st_ctime_ns)
|
|
|
|
|
|
def _artifact(record: Any, *, parse_json: bool = False) -> Any:
|
|
"""Read stable regular bytes without following links or hydrating cloud files."""
|
|
_object(record, {"path", "bytes", "sha256"})
|
|
return _read_local(_text(record["path"]), parse_json=parse_json,
|
|
expected_size=_integer(record["bytes"], 1),
|
|
expected_hash=_sha(record["sha256"]))
|
|
|
|
|
|
def _parent_fd(path: Path) -> int:
|
|
flags = os.O_RDONLY | os.O_NOFOLLOW | os.O_NONBLOCK | os.O_DIRECTORY
|
|
parent = os.open(path.anchor, flags)
|
|
try:
|
|
for part in path.parts[1:-1]:
|
|
child = os.open(part, flags, dir_fd=parent)
|
|
os.close(parent)
|
|
parent = child
|
|
return parent
|
|
except BaseException:
|
|
os.close(parent)
|
|
raise
|
|
|
|
|
|
def _read_local(raw: str, *, parse_json: bool, expected_size: int | None = None,
|
|
expected_hash: str | None = None) -> Any:
|
|
path = Path(raw)
|
|
if not path.is_absolute() or str(path) != raw or ".." in path.parts:
|
|
raise ValueError("artifact path must be canonical and absolute")
|
|
parent = descriptor = None
|
|
try:
|
|
flags = os.O_RDONLY | os.O_NOFOLLOW | os.O_NONBLOCK
|
|
parent = _parent_fd(path)
|
|
before = os.stat(path.name, dir_fd=parent, follow_symlinks=False)
|
|
if not stat.S_ISREG(before.st_mode) or getattr(before, "st_flags", 0) & 0x40000000:
|
|
raise ValueError("artifact must be a resident regular file")
|
|
if expected_size is None:
|
|
expected_size = before.st_size
|
|
if parse_json and expected_size > _MAX_JSON:
|
|
raise ValueError("JSON artifact exceeds local size limit")
|
|
if before.st_size != expected_size:
|
|
raise ValueError("artifact byte count mismatch")
|
|
descriptor = os.open(path.name, flags, dir_fd=parent)
|
|
if _identity(before) != _identity(os.fstat(descriptor)):
|
|
raise ValueError("artifact changed before reading")
|
|
digest, chunks, count = hashlib.sha256(), [], 0
|
|
while data := os.read(descriptor, 65536):
|
|
count += len(data)
|
|
if count > expected_size:
|
|
raise ValueError("artifact byte count exceeded during reading")
|
|
digest.update(data)
|
|
if parse_json:
|
|
chunks.append(data)
|
|
# Rewalk the named path: a pinned old directory fd can outlive a rename.
|
|
fresh_parent = _parent_fd(path)
|
|
try:
|
|
after = os.stat(path.name, dir_fd=fresh_parent, follow_symlinks=False)
|
|
finally:
|
|
os.close(fresh_parent)
|
|
if (_identity(before) != _identity(os.fstat(descriptor))
|
|
or _identity(before) != _identity(after)):
|
|
raise ValueError("artifact changed during reading")
|
|
if expected_hash is not None and digest.hexdigest() != expected_hash:
|
|
raise ValueError("artifact SHA-256 mismatch")
|
|
return _load_json(b"".join(chunks)) if parse_json else None
|
|
except (OSError, AttributeError) as exc:
|
|
raise ValueError("local artifact unavailable or unsafe") from exc
|
|
finally:
|
|
if descriptor is not None:
|
|
os.close(descriptor)
|
|
if parent is not None:
|
|
os.close(parent)
|
|
|
|
|
|
def load_application_request(path: str | Path) -> dict:
|
|
"""Load only a bounded resident request; never follow a config symlink."""
|
|
value = _read_local(str(Path(path).absolute()), parse_json=True)
|
|
if not isinstance(value, dict):
|
|
raise ValueError("application request must be a JSON object")
|
|
return value
|
|
|
|
|
|
def _load_json(data: bytes) -> Any:
|
|
def unique(pairs):
|
|
result = {}
|
|
for key, value in pairs:
|
|
if key in result:
|
|
raise ValueError("duplicate JSON field")
|
|
result[key] = value
|
|
return result
|
|
|
|
try:
|
|
value = json.loads(data, object_pairs_hook=unique)
|
|
_canonical(value)
|
|
return value
|
|
except (UnicodeError, RecursionError) as exc:
|
|
raise ValueError("invalid JSON artifact") from exc
|
|
|
|
|
|
def _snapshot(baseline: dict) -> dict:
|
|
_object(baseline, {"project_file", "snapshot_file", "project_name", "timeline_name",
|
|
"fps", "timeline_range"})
|
|
_rate(baseline["fps"])
|
|
bounds = _range(baseline["timeline_range"])
|
|
_artifact(baseline["project_file"])
|
|
snapshot = _artifact(baseline["snapshot_file"], parse_json=True)
|
|
if not isinstance(snapshot, dict):
|
|
raise ValueError("native snapshot must be an object")
|
|
settings = snapshot.get("settings")
|
|
native_fps = settings.get("timelineFrameRate") if isinstance(settings, dict) else None
|
|
# Resolve's conventional decimal NTSC labels represent these exact rates.
|
|
ntsc = {"23.976": "24000/1001", "29.97": "30000/1001", "59.94": "60000/1001"}
|
|
try:
|
|
if type(native_fps) not in (str, int, float):
|
|
raise ValueError("native fps missing")
|
|
label = str(native_fps)
|
|
if not re.fullmatch(r"[0-9]{1,9}(?:\.[0-9]{1,12}|/[1-9][0-9]{0,8})?", label):
|
|
raise ValueError("native fps must use a bounded decimal or rational label")
|
|
native_rate = Fraction(ntsc.get(label, label))
|
|
if native_rate != Fraction(*_rate(baseline["fps"])):
|
|
raise ValueError("native fps differs")
|
|
except (ValueError, ZeroDivisionError) as exc:
|
|
raise ValueError("native snapshot fps is missing, invalid or contradictory") from exc
|
|
for cfg, key in [("project_name", "project"), ("timeline_name", "timeline")]:
|
|
if _text(baseline[cfg]) != snapshot.get(key):
|
|
raise ValueError("native snapshot identity mismatch")
|
|
tracks = snapshot.get("timeline_readback")
|
|
if not isinstance(tracks, dict) or not tracks:
|
|
raise ValueError("native clip snapshot required")
|
|
for track, clips in tracks.items():
|
|
if not re.fullmatch(r"(?:video|audio)[1-9][0-9]*", track) or not isinstance(clips, list):
|
|
raise ValueError("invalid native snapshot track")
|
|
for clip in clips:
|
|
_check_clip(clip, bounds)
|
|
return tracks
|
|
|
|
|
|
def _check_clip(clip: Any, bounds: list[int]) -> None:
|
|
required = {"path", "name", "start", "end", "left_offset", "right_offset", "enabled", "properties"}
|
|
if not isinstance(clip, dict) or not required <= clip.keys():
|
|
raise ValueError("native snapshot clip is incomplete")
|
|
_range([clip["start"], clip["end"]], bounds)
|
|
_integer(clip["left_offset"])
|
|
_integer(clip["right_offset"])
|
|
if clip["path"] is not None:
|
|
_text(clip["path"])
|
|
_text(clip["name"])
|
|
if type(clip["enabled"]) is not bool or not isinstance(clip["properties"], dict):
|
|
raise ValueError("native clip state is incomplete")
|
|
|
|
|
|
def _binding(item: Any, tracks: dict, baseline: dict, *, audio: bool = False) -> tuple:
|
|
_object(item, {"media", "track", "clip_index", "media_frames", "fps",
|
|
"source_range", "timeline_range"})
|
|
track, index = _text(item["track"]), _integer(item["clip_index"])
|
|
if not track.startswith("audio" if audio else "video"):
|
|
raise ValueError("wrong source/audio track kind")
|
|
if track not in tracks or index >= len(tracks[track]):
|
|
raise ValueError("source binding has no native clip")
|
|
clip = tracks[track][index]
|
|
_artifact(item["media"])
|
|
if item["media"]["path"] != clip["path"]:
|
|
raise ValueError("source path does not match native clip")
|
|
if _rate(item["fps"]) != _rate(baseline["fps"]):
|
|
raise ValueError("source fps/retime ambiguity")
|
|
duration = clip["end"] - clip["start"]
|
|
capacity = _integer(item["media_frames"], 1)
|
|
if capacity != clip["left_offset"] + duration + clip["right_offset"]:
|
|
raise ValueError("source capacity does not match native offsets")
|
|
source = _range(item["source_range"], [clip["left_offset"], clip["left_offset"] + duration])
|
|
target = _range(item["timeline_range"], [clip["start"], clip["end"]])
|
|
mapped = [clip["start"] + f - clip["left_offset"] for f in source]
|
|
if mapped != target or (audio and target != [clip["start"], clip["end"]]):
|
|
raise ValueError("source/audio placement must preserve native timing")
|
|
return track, index
|
|
|
|
|
|
def _preserved_stack(protected: Any, tracks: dict, bounds: list[int]) -> list[dict]:
|
|
if not isinstance(protected, list) or not protected:
|
|
raise ValueError("protected intervals must be explicit and nonempty")
|
|
for item in protected:
|
|
_object(item, {"range", "reason"})
|
|
_range(item["range"], bounds)
|
|
_text(item["reason"])
|
|
if not any(_overlap(item["range"], [c["start"], c["end"]])
|
|
for key, clips in tracks.items() if key.startswith("video") for c in clips):
|
|
raise ValueError("protected interval has no original video stack")
|
|
return [{"track": track, "clip_index": index, "clip": copy.deepcopy(clip),
|
|
"clip_sha256": _digest(clip)}
|
|
for track, clips in sorted(tracks.items()) for index, clip in enumerate(clips)
|
|
if any(_overlap(p["range"], [clip["start"], clip["end"]]) for p in protected)]
|
|
|
|
|
|
def _candidates(items: list, source_hash: str, input_hash: str, source_url: str) -> dict:
|
|
result = {}
|
|
for item in items:
|
|
_object(item, {"id", "media", "media_frames", "fps", "origin", "relationship",
|
|
"source_sha256", "compiled_input_sha256", "review_status", "generation_receipt"})
|
|
name = _text(item["id"])
|
|
if name in result:
|
|
raise ValueError("candidate ids must be unique")
|
|
_artifact(item["media"])
|
|
_integer(item["media_frames"], 1)
|
|
_rate(item["fps"])
|
|
if item["origin"] != "provider_generated" or item["relationship"] != "generated_variation":
|
|
raise ValueError("a generated candidate cannot claim original-source identity")
|
|
if item["source_sha256"] != source_hash or item["compiled_input_sha256"] != input_hash:
|
|
raise ValueError("candidate is bound to a different source or input")
|
|
if item["review_status"] not in ("pending", "rejected", "approved"):
|
|
raise ValueError("explicit candidate review state required")
|
|
evidence = _artifact(item["generation_receipt"], parse_json=True)
|
|
if not isinstance(evidence, dict) or not isinstance(evidence.get("request_id"), str):
|
|
raise ValueError("historical request evidence required")
|
|
_text(evidence["request_id"])
|
|
expected = {"source_sha256": source_hash, "compiled_input_sha256": input_hash,
|
|
"candidate_sha256": item["media"]["sha256"], "source_url": source_url}
|
|
if any(evidence.get(key) != value for key, value in expected.items()):
|
|
raise ValueError("historical evidence does not bind the candidate source/input/bytes")
|
|
result[name] = item
|
|
return result
|
|
|
|
|
|
def _inserts(items: list, candidates: dict, config: dict, input_hash: str, edit_hash: str) -> None:
|
|
occupied = []
|
|
for item in items:
|
|
_object(item, {"candidate_id", "candidate_range", "timeline_range", "retime", "approval_file"})
|
|
candidate = candidates.get(_text(item["candidate_id"]))
|
|
if candidate is None or candidate["review_status"] != "approved":
|
|
raise ValueError("insert requires an approved, resolved candidate")
|
|
target = _range(item["timeline_range"], config["baseline"]["timeline_range"])
|
|
source = _range(item["candidate_range"], [0, candidate["media_frames"]])
|
|
if any(_overlap(target, p["range"]) for p in config["protected_intervals"]):
|
|
raise ValueError("insert overlaps protected original stack")
|
|
if any(_overlap(target, span) for span in occupied):
|
|
raise ValueError("insert proposals overlap")
|
|
if (item["retime"] != "none" or target[1] - target[0] != source[1] - source[0]
|
|
or _rate(candidate["fps"]) != _rate(config["baseline"]["fps"])):
|
|
raise ValueError("candidate fps/duration/retime ambiguity")
|
|
evidence = _artifact(item["approval_file"], parse_json=True)
|
|
expected = {"status": "approved", "candidate_sha256": candidate["media"]["sha256"],
|
|
"source_sha256": config["source"]["media"]["sha256"],
|
|
"compiled_input_sha256": input_hash,
|
|
"edit_context_sha256": edit_hash,
|
|
"candidate_range": source, "timeline_range": target}
|
|
if not isinstance(evidence, dict) or any(
|
|
_canonical(evidence.get(key)) != _canonical(value) for key, value in expected.items()):
|
|
raise ValueError("approval evidence must bind exact source, candidate and placement")
|
|
occupied.append(target)
|
|
|
|
|
|
def build_application_bundle(config: dict, compiled_input: dict | None, *, local_only: bool = False) -> dict:
|
|
"""Validate resident evidence and return a new deterministic, offline bundle."""
|
|
if type(local_only) is not bool:
|
|
raise ValueError("local_only must be an exact boolean")
|
|
_object(config, _REQUIRED, _OPTIONAL)
|
|
if len(_canonical(config)) > _MAX_JSON:
|
|
raise ValueError("application config exceeds local size limit")
|
|
if local_only:
|
|
if compiled_input is not None:
|
|
raise ValueError("local-only preservation cannot accept provider input")
|
|
else:
|
|
_object(compiled_input, {"source_video", "compiled_prompt"})
|
|
url = urlsplit(_text(compiled_input["source_video"]))
|
|
if url.scheme != "https" or not url.hostname or url.username or url.password:
|
|
raise ValueError("source reference must be HTTPS without embedded credentials")
|
|
_text(compiled_input["compiled_prompt"])
|
|
cfg = copy.deepcopy(config)
|
|
for key in ("audio", "candidates", "inserts", "historical_receipts"):
|
|
cfg.setdefault(key, [])
|
|
if not isinstance(cfg[key], list):
|
|
raise ValueError("bundle collections must be lists")
|
|
if local_only and (cfg["candidates"] or cfg["inserts"]):
|
|
raise ValueError("local-only preservation cannot contain candidates or inserts")
|
|
tracks = _snapshot(cfg["baseline"])
|
|
_binding(cfg["source"], tracks, cfg["baseline"])
|
|
audio_keys = [_binding(item, tracks, cfg["baseline"], audio=True) for item in cfg["audio"]]
|
|
expected_audio = {(t, i) for t, clips in tracks.items() if t.startswith("audio")
|
|
for i in range(len(clips))}
|
|
if len(set(audio_keys)) != len(audio_keys) or set(audio_keys) != expected_audio:
|
|
raise ValueError("every original audio clip must be preserved exactly once")
|
|
stack = _preserved_stack(cfg["protected_intervals"], tracks, cfg["baseline"]["timeline_range"])
|
|
input_hash = None if local_only else _digest(compiled_input)
|
|
edit_hash = _digest({key: cfg[key] for key in _REQUIRED})
|
|
if not local_only:
|
|
candidates = _candidates(cfg["candidates"], cfg["source"]["media"]["sha256"],
|
|
input_hash, compiled_input["source_video"])
|
|
_inserts(cfg["inserts"], candidates, cfg, input_hash, edit_hash)
|
|
for receipt in cfg["historical_receipts"]:
|
|
_artifact(receipt)
|
|
result = {**cfg, "schema_version": 1, "mode": "preserve_native_timeline",
|
|
"provider_calls": 0, "provider_execution": False, "dry_run": True, "submit": False,
|
|
"provider_input": copy.deepcopy(compiled_input), "compiled_input_sha256": input_hash,
|
|
"edit_context_sha256": edit_hash,
|
|
"protected_stack": stack, "insert_policy": "new_video_track_preserve_baseline_audio",
|
|
"evidence_scope": "verified_local_bytes_and_supplied_metadata_only"}
|
|
if local_only:
|
|
result = {**result, "local_only": True, "provider_input_status": "not_prepared_local_only",
|
|
"insert_policy": "none_preserve_baseline"}
|
|
return {**result, "bundle_sha256": _digest(result)}
|
|
|
|
|
|
def validate_application_bundle(bundle: dict) -> None:
|
|
"""Recheck all files, derived state and exact flags; no mutation or execution."""
|
|
if not isinstance(bundle, dict) or not (_REQUIRED | _OPTIONAL | {"provider_input"}) <= bundle.keys():
|
|
raise ValueError("incomplete application bundle")
|
|
cfg = {key: bundle[key] for key in _REQUIRED | _OPTIONAL}
|
|
expected = build_application_bundle(cfg, bundle["provider_input"],
|
|
local_only=bundle.get("local_only", False))
|
|
if _canonical(bundle) != _canonical(expected):
|
|
raise ValueError("application bundle differs from its bound evidence")
|