     1|"""Concrete TrueTraceShorts concept testbatch for strategy review."""
     2|
     3|from __future__ import annotations
     4|
     5|from autoshorts.ideas.candidate import VideoCandidate
     6|from autoshorts.ideas.testbatch import TestBatch
     7|from autoshorts.scripting.quality import ScriptQualityScore
     8|from autoshorts.strategy.brand import TRUETRACE_SHORTS_BRAND, apply_brand_metadata
     9|
    10|
    11|def _score(*, hook: int = 9, novelty: int = 9, clarity: int = 9, retention: int = 8) -> ScriptQualityScore:
    12|    return ScriptQualityScore(
    13|        hook_strength=hook,
    14|        novelty=novelty,
    15|        clarity=clarity,
    16|        retention=retention,
    17|        ai_slop_risk=2,
    18|        policy_risk="low",
    19|    )
    20|
    21|
    22|def _candidate(
    23|    *,
    24|    candidate_id: str,
    25|    title: str,
    26|    duration_seconds: int,
    27|    hook: str,
    28|    script_outline: str,
    29|    visual_concept: str,
    30|    caption: str,
    31|    cta: str,
    32|    hypothesis: str,
    33|    hashtags: tuple[str, ...] = ("#AI", "#AISystems", "#Workflow"),
    34|) -> VideoCandidate:
    35|    return apply_brand_metadata(
    36|        VideoCandidate(
    37|            candidate_id=candidate_id,
    38|            pillar="ai_life_systems",
    39|            title=title,
    40|            platforms=("youtube", "tiktok"),
    41|            duration_seconds=duration_seconds,
    42|            hook=hook,
    43|            script_outline=script_outline,
    44|            visual_concept=visual_concept,
    45|            caption=caption,
    46|            cta=cta,
    47|            hashtags=hashtags,
    48|            quality_score=_score(),
    49|            policy_notes=("Concept testbatch: concrete TrueTrace direction",),
    50|            hypothesis=hypothesis,
    51|        ),
    52|        TRUETRACE_SHORTS_BRAND,
    53|    )
    54|
    55|
    56|def build_truetrace_concept_batch() -> TestBatch:
    57|    """Build five concrete public-safe TrueTraceShorts concept candidates."""
    58|
    59|    return TestBatch(
    60|        batch_id="truetrace-concept-testbatch-001",
    61|        candidates=(
    62|            _candidate(
    63|                candidate_id="truetrace-ai-automation-quiet-rot-38s",
    64|                title="Why Most AI Automations Break After Three Days",
    65|                duration_seconds=38,
    66|                hook="Most AI automations do not fail dramatically. They quietly rot.",
    67|                script_outline=(
    68|                    "Show a day-one workflow turning from green to red over a week. Reveal that the missing system piece "
    69|                    "is an exception path for weird inputs, uncertain outputs, review, and stop conditions. End with the rule: "
    70|                    "no exception path means fragile automation."
    71|                ),
    72|                visual_concept=(
    73|                    "Dark timeline Day 1, Day 3, Day 7; output cards degrade from green to red; split diagram for normal, "
    74|                    "weird, and uncertain input; final failure/autopsy card."
    75|                ),
    76|                caption="AI automations rarely explode. They quietly rot when the workflow has no exception path.",
    77|                cta="Follow for practical AI systems, not automation theatre.",
    78|                hypothesis="A failure autopsy with the phrase quietly rot should create stronger retention than generic AI automation tips.",
    79|            ),
    80|            _candidate(
    81|                candidate_id="truetrace-prompt-vs-system-workflow-breaks-35s",
    82|                title="This Prompt Looks Smart — But the Workflow Still Breaks",
    83|                duration_seconds=35,
    84|                hook="This is why your perfect AI prompt still fails tomorrow.",
    85|                script_outline=(
    86|                    "Open with a glowing prompt card, then show it failing because the surrounding system is missing. Contrast prompt asks "
    87|                    "with system checks: allowed input, useful output, review step, and action boundary. End with prompt is not system."
    88|                ),
    89|                visual_concept=(
    90|                    "Prompt card collapses into warnings; Input to AI to Review to Action flow; missing review gate highlighted; "
    91|                    "final Systems over prompts rule card."
    92|                ),
    93|                caption="A better prompt will not fix a broken workflow. Draw the system boundary first.",
    94|                cta="Follow for systems over prompts.",
    95|                hypothesis="Direct anti-prompt positioning should attract viewers tired of generic prompt-hack content.",
    96|            ),
    97|            _candidate(
    98|                candidate_id="truetrace-ai-notes-equal-importance-34s",
    99|                title="The Hidden Reason Your AI Notes Become Useless",
   100|                duration_seconds=34,
   101|                hook="AI notes fail when everything becomes equally important.",
   102|                script_outline=(
   103|                    "Show a pretty AI notes page flooding with equal bullet points. Reveal the mechanism: summaries fail when facts, "
   104|                    "decisions, risks, questions, and next actions have the same weight. End with ask for decision structure, not notes."
   105|                ),
   106|                visual_concept=(
   107|                    "Pretty notes card; bullet flood; equality warning; five-column structure: facts, decisions, risks, questions, actions; "
   108|                    "final rule card."
   109|                ),
   110|                caption="Pretty AI notes are not the same as useful decisions. Ask for structure, not notes.",
   111|                cta="Follow for AI workflows that survive real use.",
   112|                hypothesis="A daily productivity pain point should broaden reach beyond hard technical AI audiences.",
   113|            ),
   114|            _candidate(
   115|                candidate_id="truetrace-ai-agents-tasks-not-teams-40s",
   116|                title="Claim: AI Agents Will Replace Teams",
   117|                duration_seconds=40,
   118|                hook="Verdict: not unless they can handle boundaries.",
   119|                script_outline=(
   120|                    "Use AI Hype Court format: claim, evidence, problem, verdict, better action. Agents can draft, summarize, search, and trigger, "
   121|                    "but ownership requires priorities, exceptions, tradeoffs, accountability, and messy context. Verdict: tasks first, not teams."
   122|                ),
   123|                visual_concept=(
   124|                    "Hype Court stamp; claim card; evidence scale; responsibility gap warning; verdict stamp reading tasks, not teams."
   125|                ),
   126|                caption="AI agents can replace tasks first. They do not automatically replace teams.",
   127|                cta="Follow for AI hype checks with useful verdicts.",
   128|                hypothesis="A skeptical verdict on a popular AI claim should invite comments while staying public-safe.",
   129|                hashtags=("#AI", "#AIAgents", "#AISystems", "#Workflow"),
   130|            ),
   131|            _candidate(
   132|                candidate_id="truetrace-review-before-automation-32s",
   133|                title="One Rule Before You Automate Anything With AI",
   134|                duration_seconds=32,
   135|                hook="If you cannot review it manually, do not automate it yet.",
   136|                script_outline=(
   137|                    "State one rule, then one safe example: feedback to AI summary to checklist to decision. Show why missing complaints, "
   138|                    "invented patterns, and loud-comment bias need a review checklist before automation."
   139|                ),
   140|                visual_concept=(
   141|                    "Rule card; bad workflow in red; better workflow with checklist; three checklist warnings; final Review before automation card."
   142|                ),
   143|                caption="Automate after you can review. Not before.",
   144|                cta="Follow for simple rules for safer AI systems.",
   145|                hypothesis="A compact operating rule should be saveable and build trust even if it is less viral than failure-led hooks.",
   146|            ),
   147|        ),
   148|    )
   149|
   150|
   151|def render_truetrace_concept_summary() -> str:
   152|    """Render the five-concept testbatch for human strategy review without action language."""
   153|
   154|    batch = build_truetrace_concept_batch()
   155|    lines = [
   156|        "## TrueTrace Concept Testbatch",
   157|        f"Batch: {batch.batch_id}",
   158|        f"Candidates: {len(batch.candidates)}",
   159|        f"Brand: {TRUETRACE_SHORTS_BRAND.channel_name} — {TRUETRACE_SHORTS_BRAND.tagline}",
   160|        "",
   161|    ]
   162|    for index, candidate in enumerate(batch.candidates, start=1):
   163|        youtube_description = f"{candidate.caption}\n\n{candidate.cta}\n\n{' '.join(candidate.hashtags)}"
   164|        tiktok_caption = f"{candidate.caption} {' '.join(candidate.hashtags)}"
   165|        lines.extend(
   166|            [
   167|                f"## {index}. {candidate.title}",
   168|                f"ID: {candidate.candidate_id}",
   169|                f"Duration: {candidate.duration_seconds}s",
   170|                f"Hook: {candidate.hook}",
   171|                f"Outline: {candidate.script_outline}",
   172|                f"Visuals: {candidate.visual_concept}",
   173|                f"Hypothesis: {candidate.hypothesis}",
   174|                f"YouTube: {youtube_description}",
   175|                f"TikTok: {tiktok_caption}",
   176|                f"Tags: {', '.join(TRUETRACE_SHORTS_BRAND.youtube_tags)}",
   177|                "",
   178|            ]
   179|        )
   180|    return "\n".join(lines).strip()
   181|


from autoshorts.social.final_approval import (
    FinalVideoApprovalPackage,
    build_truetrace_weird_email_final_approval_package,
    render_final_video_approval_review,
    validate_final_video_approval_package,
)

def test_build_truetrace_weird_email_final_approval_package_is_approval_first():
    package = build_truetrace_weird_email_final_approval_package(
        media_path="data/final_exports/truetrace/final.mp4",
        checksum_sha256="2e02b2960b1924abd8bc4154d91f521b370dde52f2af0944b24723c453fd0c6b",
    )

    assert isinstance(package, FinalVideoApprovalPackage)
    assert package.candidate_id == "truetrace-ai-automation-quiet-rot-38s"
    assert package.media_path.endswith("final.mp4")
    assert package.checksum_sha256 == "2e02b2960b1924abd8bc4154d91f521b370dde52f2af0944b24723c453fd0c6b"
    assert package.platforms == ("youtube", "tiktok")
    assert package.platform_labels == ("YouTube Shorts", "TikTok")
    assert package.final_video_language == "en"
    assert package.approval_language == "de"
    assert package.duration_seconds == 37
    assert package.resolution == "1080x1920"
    assert package.requires_human_approval is True
    assert package.side_effects == ()
    assert "weird email" in package.hook.casefold()
    assert "exception path" not in package.caption.casefold()
    assert package.hashtags == ("#AI", "#AISystems", "#Workflow")

def test_render_final_video_approval_review_is_telegram_safe_and_no_action_language():
    package = build_truetrace_weird_email_final_approval_package(
        media_path="data/final_exports/truetrace/final.mp4",
        checksum_sha256="2e02b2960b1924abd8bc4154d91f521b370dde52f2af0944b24723c453fd0c6b",
    )

    text = render_final_video_approval_review(package)

    assert "## Final Video Review" in text
    assert "Freigabe YouTube" in text
    assert "Freigabe TikTok" in text
    assert "Freigabe alle" in text
    assert "Ablehnen" in text
    assert "Ändern:" in text
    assert "data/final_exports/truetrace/final.mp4" in text
    assert package.checksum_sha256 in text
    assert len(text) < 4096
    assert "publish" not in text.casefold()
    assert "post" not in text.casefold()

def test_validate_final_video_approval_package_rejects_unsafe_fields():
    package = FinalVideoApprovalPackage(
        candidate_id="",
        media_path="final.mov",
        checksum_sha256="bad",
        platforms=("linkedin",),
        platform_labels=("LinkedIn",),
        title_variants=(),
        caption="",
        hashtags=(),
        hook="",
        duration_seconds=9,
        resolution="540x960",
        quality_notes="",
        approval_language="en",
        final_video_language="de",
        requires_human_approval=False,
        side_effects=("send_telegram",),
    )

    result = validate_final_video_approval_package(package)

    assert result.is_valid is False
    assert "candidate_id is required" in result.errors
    assert "media_path must end with .mp4" in result.errors
    assert "checksum_sha256 must be a 64-character hex string" in result.errors
    assert "unsupported platform: linkedin" in result.errors
    assert "at least one title variant is required" in result.errors
    assert "caption is required" in result.errors
    assert "human approval is required" in result.errors
    assert "final video approval packages must not have side effects" in result.errors


LSP diagnostics introduced by this edit:
<diagnostics file="/home/agent/jarvis_runtime/autoshortsbot/AutoShortsBot/tests/test_final_video_approval.py">
ERROR [1:6] Import "autoshorts.social.final_approval" could not be resolved [reportMissingImports] (Pyright)
</diagnostics>


terminal

Output:
==================================== ERRORS ====================================
_____________ ERROR collecting tests/test_final_video_approval.py ______________
ImportError while importing test module '/home/agent/jarvis_runtime/autoshortsbot/AutoShortsBot/tests/test_final_video_approval.py'.
Hint: make sure your test modules/packages have valid Python names.
Traceback:
../../../.local/share/uv/python/cpython-3.11.15-linux-x86_64-gnu/lib/python3.11/importlib/__init__.py:126: in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
tests/test_final_video_approval.py:1: in <module>
    from autoshorts.social.final_approval import (
E   ModuleNotFoundError: No module named 'autoshorts.social.final_approval'
=========================== short test summary info ============================
ERROR tests/test_final_video_approval.py
!!!!!!!!!!!!!!!!!!!! Interrupted: 1 error during collection !!!!!!!!!!!!!!!!!!!!
1 error in 0.13s


"""Approval-first package for final-near rendered videos."""

from __future__ import annotations

from dataclasses import dataclass
import re

from autoshorts.ideas.candidate import SUPPORTED_PLATFORMS, ValidationResult

_PLATFORM_LABELS = {
    "youtube": "YouTube Shorts",
    "tiktok": "TikTok",
    "instagram": "Instagram Reels",
}
_SHA256_RE = re.compile(r"^[a-fA-F0-9]{64}$")

@dataclass(frozen=True)
class FinalVideoApprovalPackage:
    candidate_id: str
    media_path: str
    checksum_sha256: str
    platforms: tuple[str, ...]
    platform_labels: tuple[str, ...]
    title_variants: tuple[str, ...]
    caption: str
    hashtags: tuple[str, ...]
    hook: str
    duration_seconds: int
    resolution: str
    quality_notes: str
    approval_language: str = "de"
    final_video_language: str = "en"
    requires_human_approval: bool = True
    side_effects: tuple[str, ...] = ()

def _is_blank(value: str) -> bool:
    return not (value or "").strip()

def build_truetrace_weird_email_final_approval_package(*, media_path: str, checksum_sha256: str) -> FinalVideoApprovalPackage:
    package = FinalVideoApprovalPackage(
        candidate_id="truetrace-ai-automation-quiet-rot-38s",
        media_path=media_path,
        checksum_sha256=checksum_sha256,
        platforms=("youtube", "tiktok"),
        platform_labels=("YouTube Shorts", "TikTok"),
        title_variants=(
            "Your AI Automation Works Until One Weird Email Arrives",
            "Why AI Automations Break When Real Life Shows Up",
        ),
        caption=(
            "Your AI automation does not need a smarter prompt first. It needs a simple plan for weird inputs: "
            "what goes through, what gets reviewed, and what must stop."
        ),
        hashtags=("#AI", "#AISystems", "#Workflow"),
        hook="Your AI automation works perfectly in the demo. Then one weird email arrives.",
        duration_seconds=37,
        resolution="1080x1920",
        quality_notes=(
            "Beginner-friendly weird-email framing; renderer-owned labels and captions; "
            "scene-specific text-free ChatGPT Image backgrounds; compact phrase-boundary subtitles."
        ),
        approval_language="de",
        final_video_language="en",
        requires_human_approval=True,
        side_effects=(),
    )
    validation = validate_final_video_approval_package(package)
    if not validation.is_valid:
        raise ValueError("; ".join(validation.errors))
    return package

def validate_final_video_approval_package(package: FinalVideoApprovalPackage) -> ValidationResult:
    errors: list[str] = []
    for field_name, value in {
        "candidate_id": package.candidate_id,
        "media_path": package.media_path,
        "checksum_sha256": package.checksum_sha256,
        "caption": package.caption,
        "hook": package.hook,
        "resolution": package.resolution,
        "quality_notes": package.quality_notes,
        "approval_language": package.approval_language,
        "final_video_language": package.final_video_language,
    }.items():
        if _is_blank(value):
            errors.append(f"{field_name} is required")

    if package.media_path and not package.media_path.endswith(".mp4"):
        errors.append("media_path must end with .mp4")
    if package.checksum_sha256 and not _SHA256_RE.match(package.checksum_sha256):
        errors.append("checksum_sha256 must be a 64-character hex string")
    if not package.platforms:
        errors.append("at least one platform is required")
    for platform in package.platforms:
        if platform not in SUPPORTED_PLATFORMS:
            errors.append(f"unsupported platform: {platform}")
    if len(package.platform_labels) != len(package.platforms):
        errors.append("platform_labels must match platforms")
    for platform, label in zip(package.platforms, package.platform_labels):
        expected = _PLATFORM_LABELS.get(platform)
        if expected is not None and label != expected:
            errors.append(f"platform label for {platform} must be {expected}")
    if not package.title_variants:
        errors.append("at least one title variant is required")
    elif any(_is_blank(title) for title in package.title_variants):
        errors.append("title variants must not be blank")
    if not package.hashtags:
        errors.append("at least one hashtag is required")
    elif any(_is_blank(hashtag) for hashtag in package.hashtags):
        errors.append("hashtags must not be blank")
    if not 10 <= package.duration_seconds <= 180:
        errors.append("duration_seconds must be between 10 and 180")
    if package.resolution != "1080x1920":
        errors.append("resolution must be 1080x1920")
    if package.approval_language != "de":
        errors.append("approval_language must be de")
    if package.final_video_language != "en":
        errors.append("final_video_language must be en")
    if not package.requires_human_approval:
        errors.append("human approval is required")
    if package.side_effects:
        errors.append("final video approval packages must not have side effects")
    return ValidationResult(is_valid=not errors, errors=tuple(errors))

def _platform_command(platform: str) -> str:
    if platform == "youtube":
        return "Freigabe YouTube"
    if platform == "tiktok":
        return "Freigabe TikTok"
    if platform == "instagram":
        return "Freigabe Instagram"
    return f"Freigabe {platform}"

def render_final_video_approval_review(package: FinalVideoApprovalPackage) -> str:
    validation = validate_final_video_approval_package(package)
    if not validation.is_valid:
        raise ValueError("; ".join(validation.errors))

    titles = "\n".join(f"- {title}" for title in package.title_variants)
    commands = "\n".join(
        f"- {command}"
        for command in (
            *(_platform_command(platform) for platform in package.platforms),
            "Freigabe alle",
            "Ablehnen",
            "Ändern: <gewünschte Änderung>",
        )
    )
    text = "\n".join(
        (
            "## Final Video Review",
            "",
            f"Candidate: {package.candidate_id}",
            f"Media: {package.media_path}",
            f"SHA256: {package.checksum_sha256}",
            f"Platforms: {', '.join(package.platform_labels)}",
            f"Language: final={package.final_video_language}, review={package.approval_language}",
            f"Duration: {package.duration_seconds}s",
            f"Resolution: {package.resolution}",
            "",
            "Hook:",
            package.hook,
            "",
            "Title variants:",
            titles,
            "",
            "Caption:",
            package.caption,
            "",
            f"Hashtags: {' '.join(package.hashtags)}",
            f"Quality notes: {package.quality_notes}",
            "Requires human approval: yes",
            "Side effects: none",
            "",
            "Approval commands:",
            commands,
        )
    )
    lowered = text.casefold()
    if "publish" in lowered or "post" in lowered:
        raise ValueError("final video review must not imply publishing")
    if len(text) > 4096:
        raise ValueError("final video review exceeds Telegram limit")
    return text


Output:
...                                                                      [100%]
3 passed in 0.04s


Explanation:
**Evaluating approval process**

I need to generate real approval text and possibly send media in the final package. It feels important to update the docs and readme too. I'm considering if I should create a CLI, but maybe continuing with just the final approval package will be enough for now. I should also test the review with an actual hash, and I need to run a script to render the text.

python - <<'PY'
from autoshorts.social.final_approval import build_truetrace_weird_email_final_approval_package, render_final_video_approval_review
media='data/final_exports/truetrace-final-motion-v7-1080/truetrace-ai-automation-quiet-rot-38s/final-motion-audio-caption-1080.mp4'
sha='2e02b2960b1924abd8bc4154d91f521b370dde52f2af0944b24723c453fd0c6b'
package=build_truetrace_weird_email_final_approval_package(media_path=media, checksum_sha256=sha)
text=render_final_video_approval_review(package)
print(text)
print('\nTEXT_LENGTH', len(text))
PY


Output:
## Final Video Review

Candidate: truetrace-ai-automation-quiet-rot-38s
Media: data/final_exports/truetrace-final-motion-v7-1080/truetrace-ai-automation-quiet-rot-38s/final-motion-audio-caption-1080.mp4
SHA256: 2e02b2960b1924abd8bc4154d91f521b370dde52f2af0944b24723c453fd0c6b
Platforms: YouTube Shorts, TikTok
Language: final=en, review=de
Duration: 37s
Resolution: 1080x1920

Hook:
Your AI automation works perfectly in the demo. Then one weird email arrives.

Title variants:
- Your AI Automation Works Until One Weird Email Arrives
- Why AI Automations Break When Real Life Shows Up

Caption:
Your AI automation does not need a smarter prompt first. It needs a simple plan for weird inputs: what goes through, what gets reviewed, and what must stop.

Hashtags: #AI #AISystems #Workflow
Quality notes: Beginner-friendly weird-email framing; renderer-owned labels and captions; scene-specific text-free ChatGPT Image backgrounds; compact phrase-boundary subtitles.
Requires human approval: yes
Side effects: none

Approval commands:
- Freigabe YouTube
- Freigabe TikTok
- Freigabe alle
- Ablehnen
- Ändern: <gewünschte Änderung>

TEXT_LENGTH 1129


Explanation:
**Verifying media files**

I need to ensure that I have the correct absolute path for the media files. It’s important for me to verify that the files exist and check their SHA hash. I plan to use the terminal, as it’s essential for running mandatory hashes, but it might be worth verifying everything again just to be certain. I want to double-check to ensure everything is accurate and in order!


Output:
2e02b2960b1924abd8bc4154d91f521b370dde52f2af0944b24723c453fd0c6b  /home/agent/jarvis_runtime/autoshortsbot/AutoShortsBot/data/final_exports/truetrace-final-motion-v7-1080/truetrace-ai-automation-quiet-rot-38s/final-motion-audio-caption-1080.mp4
codec_name=h264
codec_type=video
width=1080
height=1920
duration=36.833333
codec_name=aac
codec_type=audio
duration=36.864000


Need docs.

Need docs.

patch

- `autoshorts.social.delivery` — side-effect-free delivery preparation for social draft review messages.
- `autoshorts.social.final_approval` — final-video approval packages with media path, checksum, platform metadata, and Telegram-safe review copy.


--- a//home/agent/jarvis_runtime/autoshortsbot/AutoShortsBot/README.md
+++ b//home/agent/jarvis_runtime/autoshortsbot/AutoShortsBot/README.md
@@ -46,7 +46,8 @@
 - `autoshorts.rendering.preview` — side-effect-free HTML timeline previews for render manifests.
 - `autoshorts.rendering.shot_plan` — retention-led shot planner before render manifests or video generation.
 - `autoshorts.review.package` — Telegram-friendly review package renderer for human approval.
-- `autoshorts.social.delivery` — side-effect-free delivery plan for social media draft review text.
+- `autoshorts.social.delivery` — side-effect-free delivery preparation for social draft review messages.
+- `autoshorts.social.final_approval` — final-video approval packages with media path, checksum, platform metadata, and Telegram-safe review copy.
 - `autoshorts.social.draft` — approval-first social media draft model from candidate plus render preview.
 - `autoshorts.social.manual_posting` — side-effect-free manual upload copy pack for phase-1 YouTube/TikTok metadata.
 - `autoshorts.social.review` — Telegram-safe approval text renderer for social media drafts.

No linter for .md files

/home/agent/jarvis_runtime/autoshortsbot/AutoShortsBot/README.md was modified by sibling subagent '20260524_234753_c12098' but this agent never read it. Read the file before writing to avoid overwriting the sibling's changes.


The 1080 three-way split frame was visually checked for mobile readability, subtitle placement, and clipping.

## V8 approval package

`autoshorts.social.final_approval` prepares a side-effect-free final video approval package for the rendered candidate. It includes:

```text
media path
SHA256 checksum
platform labels
title variants
caption/hashtags
language metadata
quality notes
approval commands
```

The rendered review text stays below Telegram's limit and avoids implicit action words such as `publish` or `post`. It does not send anything; Telegram/media delivery remains an explicit human-approved boundary.


--- a//home/agent/jarvis_runtime/autoshortsbot/AutoShortsBot/docs/final_motion_prototype.md
+++ b//home/agent/jarvis_runtime/autoshortsbot/AutoShortsBot/docs/final_motion_prototype.md
@@ -108,3 +108,20 @@
 ```

 The 1080 three-way split frame was visually checked for mobile readability, subtitle placement, and clipping.
+
+## V8 approval package
+
+`autoshorts.social.final_approval` prepares a side-effect-free final video approval package for the rendered candidate. It includes:
+
+```text
+media path
+SHA256 checksum
+platform labels
+title variants
+caption/hashtags
+language metadata
+quality notes
+approval commands
+```
+
+The rendered review text stays below Telegram's limit and avoids implicit action words such as `publish` or `post`. It does not send anything; Telegram/media delivery remains an explicit human-approved boundary.

No linter for .md files

/home/agent/jarvis_runtime/autoshortsbot/AutoShortsBot/docs/final_motion_prototype.md was modified by sibling subagent '20260524_234753_c12098' but this agent never read it. Read the file before writing to avoid overwriting the sibling's changes.


terminal

Output:
........................................................................ [ 41%]
........................................................................ [ 83%]
............................                                             [100%]
172 passed in 32.86s


git status --short && git add autoshorts/social/final_approval.py tests/test_final_video_approval.py README.md docs/final_motion_prototype.md && git commit -m "feat: add final video approval package" && git status --short && git log --oneline -5


Output:
M README.md
 M docs/final_motion_prototype.md
?? autoshorts/social/final_approval.py
?? tests/test_final_video_approval.py
[feat/core-approval-quality-gates 8598dcd] feat: add final video approval package
 4 files changed, 292 insertions(+), 1 deletion(-)
 create mode 100644 autoshorts/social/final_approval.py
 create mode 100644 tests/test_final_video_approval.py
8598dcd feat: add final video approval package
033334c feat: compact final motion subtitles
db200ae feat: add scene-specific motion backgrounds
14310f9 feat: add TrueTrace final motion prototype
7375311 feat: make TrueTrace pilot beginner friendly


Erledigt, Sir.

Ich habe jetzt den nächsten sauberen Schritt gebaut: **Final Video Approval Package** — also nicht nur ein MP4, sondern ein prüfbares Freigabe-Paket mit Metadata, Checksums, Titelvarianten, Caption, Hashtags und Approval Commands.

## Neuer Commit

```text
8598dcd feat: add final video approval package
```

## Neu gebaut

```text
autoshorts/social/final_approval.py
tests/test_final_video_approval.py
```

## Was das Package enthält

```text
Candidate-ID
Media-Pfad
SHA256
Plattformen
Titelvarianten
Caption
Hashtags
Hook
Dauer
Resolution
Quality Notes
Freigabe-Kommandos
```

Alles weiterhin:

```text
requires_human_approval=True
side_effects=()
keine Telegram/API Calls
kein Upload
```

Also keine heimliche Content-Katapultierung. Sehr zivilisiert.

---

## Final Video Review Package

MEDIA:/home/agent/jarvis_runtime/autoshortsbot/AutoShortsBot/data/final_exports/truetrace-final-motion-v7-1080/truetrace-ai-automation-quiet-rot-38s/final-motion-audio-caption-1080.mp4

## Metadata

Candidate:

```text
truetrace-ai-automation-quiet-rot-38s
```

SHA256 verifiziert:

```text
2e02b2960b1924abd8bc4154d91f521b370dde52f2af0944b24723c453fd0c6b
```

Video:

```text
Resolution: 1080x1920
Duration: 36.833s
Audio duration: 36.864s
Video codec: h264
Audio codec: aac
```

Platforms:

```text
YouTube Shorts
TikTok
```

Final language:

```text
English
```

Review language:

```text
German
```

---

## Titelvarianten

```text
Your AI Automation Works Until One Weird Email Arrives
```

```text
Why AI Automations Break When Real Life Shows Up
```

## Caption

```text
Your AI automation does not need a smarter prompt first. It needs a simple plan for weird inputs: what goes through, what gets reviewed, and what must stop.
```

## Hashtags

```text
#AI #AISystems #Workflow
```

## Hook

```text
Your AI automation works perfectly in the demo. Then one weird email arrives.
```

## Quality Notes

```text
Beginner-friendly weird-email framing;
renderer-owned labels and captions;
scene-specific text-free ChatGPT Image backgrounds;
compact phrase-boundary subtitles.
```

---

## Approval Commands

Wenn Sie ihn freigeben wollen:

```text
Freigabe YouTube
```

```text
Freigabe TikTok
```

```text
Freigabe alle
```

Oder:

```text
Ablehnen
```

```text
Ändern: <gewünschte Änderung>
```

## Tests

Full Suite:

```text
172 passed in 32.86s
```

## Aktueller Commit-Verlauf

```text
8598dcd feat: add final video approval package
033334c feat: compact final motion subtitles
db200ae feat: add scene-specific motion backgrounds
14310f9 feat: add TrueTrace final motion prototype
7375311 feat: make TrueTrace pilot beginner friendly
```

Der Clip ist jetzt technisch und organisatorisch in einem Zustand, wo er sauber reviewed werden kann: Video + Social Metadata + Freigabe-Sprache + Checksums.