OUTPUT #692 | 17773 karakter | 346 satır
========== YENİ KOMUT | 16.09.2026 22:49:39 ==========


========== YENİ KOMUT | 16.09.2026 22:49:39 ==========

/home/hermes/.hermes/skills/media/shorts-core/SKILL.md
77-- İlk kelime/heceyi korumak için sesin başına en az **150–200 ms**, sonuna **200 ms** sessizlik ekle. TTS’nin kendi sessizliği olsa bile padding’i doğrula.
78-- Padding sonrası altyazı ve sahne zamanlarını aynı miktarda kaydır.
79-- TTS’yi tekrar sıkıştırmak yerine mümkünse padding’li WAV’ı Montaj’a ver.
80-
81-### 5. Statik slayt hissini engelle
82-
83-- Pan/zoom verilmiş fotoğrafı gerçek footage yerine koyma. Fotoğraf zorunluysa destekleyici ve kısa kullan; annotation, belge detayı veya gerçek footage ile bağlantı kur.
84-- Aynı görsel kompozisyonunu yaklaşık **2–3 saniyeden uzun** kesintisiz tutmayı QA uyarısı say; fakat otomatik kesme reçetesi yapma. Uzun cümleyi anlamlı alt beat’lere böl ve gerçek hareket varsa planı yalnız süre doldu diye kesme.
85-- Rastgele hızlı kesme yapma. Kesme noktaları cümle, yan cümle, vurgu veya anlatı beat’iyle eşleşmeli.
86-- Aynı kaynak görsel yeniden kullanılacaksa farklı crop, focal point, zoom yönü veya renk tonu ile yeni bir kompozisyon oluştur.
87:- Hareketin gerçekten var olduğunu `freezedetect` ve farklı zamanlardan kareler ile doğrula.
88-
89-### 6. Anlamlı grafik ve animasyon
90-
91-- Anlatımda **süreç, sıralama, karşılaştırma, mekanizma veya neden-sonuç** varsa yalnızca fotoğraf gösterme; sesle senkron açıklayıcı grafik/animasyon üret.
92-- Grafik yalnızca estetik süs değil, cümlenin anlamını görünür kılmalı.
93-- Örnek eşlemeler:
94-  - süreç → aşamalar ve ilerleyen parçacık/ok;
95-  - sıralama → maddelerin sesle sırayla görünmesi;
96-  - karşılaştırma → iki taraflı ölçü/önce-sonra;
97-  - neden-sonuç → kaynak, akış ve sonuç vurgusu;
--
214-`/home/hermes/.hermes/tools/montaj/.venv/bin/python`
215-
216-Helper şu işleri tek deterministic zincirde yürütür:
217-
218-- seçilmiş Türkçe TTS + padding;
219-- VTT tabanlı en fazla iki satır ASS altyazı;
220-- full final encode öncesi gerçek glyph bbox guardrail;
221-- seçilmiş shot'lardan tek 1080×1920 visual master;
222-- hazır master + ses için FFmpeg direct mux ve `-c:v copy`;
223-- video stream hash kontrolü;
224:- probe, decode, black/freeze ve loudness QA;
225-- tek final contact sheet;
226:- TTS, bbox, visual master, final mux ve deterministic QA cache.
227-
228-Değişmeyen manifest ve girdilerde cache'i yeniden kullan. Aynı deterministic adımı doğrulama amacıyla tekrar üretme.
229-
230-Helper için geçici tek-seferlik bbox scripti, yeni Python ortamı veya yeniden keşfedilmiş FFmpeg/Montaj yolları oluşturma. Helper teknik olarak başarısız olursa yalnız hatalı bileşeni teşhis et.
231-
232-### Faz 3 — Tek görsel QA geçidi
233-
234-Deterministic pipeline PASS verdikten sonra yalnız final contact sheet'i `vision_analyze` ile değerlendir.
235-
236-Kontrol et:
--
267-- Altyazıyı üç satıra zorlamak ya da kenara kadar uzatmak.
268-- Whisper’ın yanlış Türkçe kelimelerini altyazıya aynen taşımak.
269-- TTS hızını agresif artırmak veya ilk heceyi fade/padding olmadan kesmek.
270-- Tek fotoğrafı 5–10 saniye küçük bir zoom bile olmadan göstermek.
271-- Anlamlı bir mekanizmayı yalnızca dekoratif parçacıklarla geçiştirmek.
272-- Bbox/preflight guardrail geçmeden pahalı final render’a girmek.
273-- Hazır video master’ı yalnız ses eklemek için Montaj’da yeniden encode etmek.
274-- Değişmeyen source-sheet, shot, annotation veya QA çıktısını cache varken yeniden üretmek.
275-- Bilinen workspace varken tüm home dizininde tekrar asset aramak veya üretim içinde tek-seferlik bbox/QA scriptleri yeniden yazmak.
276-- Background süreçte PATH’e güvenip `montaj: command not found` hatasına düşmek.
277:- Başarılı tool çıkışını teslim başarısı saymak; final MP4’ü yeniden probe ve görsel QA ile doğrulamak.
278-
279-## Doğrulama Geçidi
280-
281-Teslimden önce bunların tamamı doğru olmalı:
282-
283-- [ ] Video 1080×1920, 9:16 ve tüm kare aktif içerikle dolu.
284-- [ ] Siyah/boş üst-alt alan yok.
285-- [ ] Bütün altyazılar en fazla 2 satır ve %8 safe area içinde.
286-- [ ] Hiçbir metin veya kutu kırpılmıyor.
287-- [ ] Türkçe TTS doğal hızda; ilk/son kelime kesilmiyor; padding doğrulandı.
--
290-- [ ] Sahne grameri konuya uygun çeşitleniyor; grid/kart/easing/kompozisyon mekanik biçimde tekrarlanmıyor.
291-- [ ] Grafik ve animasyon görünmeyen mekanizmayı açıklıyor; gerçek görüntünün yerine geçmiyor ve ona anlamlı geçişle bağlanıyor.
292-- [ ] Büyük vurgu metni altyazıyı gereksiz yere tekrar etmiyor.
293-- [ ] TTS metni konuşma dilinde; en az iki ücretsiz Türkçe ses karşılaştırılmış, ritim/durak/vurgu doğal ve ilk/son hece kesilmiyor.
294-- [ ] İlk kare/cümle anlaşılır vaat ve bilgi açığı kuruyor; final payoff bu vaadi karşılıyor.
295-- [ ] Her kesmenin anlatısal görevi var; aşırı bilişsel yük veya rastgele hızlandırma yok.
296-- [ ] Sahne değişimleri ve grafikler anlatım zamanlarıyla eşleşiyor; bakış yönü korunuyor.
297-- [ ] Süreç/sıralama/karşılaştırma/neden-sonuç anlatımı anlamlı animasyonla destekleniyor.
298-- [ ] Müzik konuşmayı bastırmıyor; her beat’te ses odağı belirgin ve clipping yok.
299-- [ ] Assembly yolu doğru seçildi: direct mux mümkünse video stream yeniden encode edilmedi; Montaj gerekiyorsa validate başarılı.
300:- [ ] Final MP4 probe, decode, black/freeze, loudness ve contact-sheet QA’dan geçti.
301-- [ ] Kullanıcıya kısa Türkçe rapor ve gerçek MP4 dosyası gönderiliyor.

/home/hermes/.hermes/plugins/shorts-production-guard/__init__.py
335-            "mode": gate.get("mode"),
336-            "candidates": normalized,
337-        },
338-        "selected_casting": selected_casting,
339-    }
340-    return hashlib.sha256(
341-        json.dumps(payload, sort_keys=True, ensure_ascii=False).encode("utf-8")
342-    ).hexdigest()
343-
344-
345:def _proof_freeze_block(tool_name: str, args: dict) -> Optional[dict]:
346:    """After a valid proof PASS, prevent edits that invalidate the accepted proof."""
347-    if tool_name not in {"patch", "write_file", "execute_code"}:
348-        return None
349-
350-    if tool_name == "execute_code":
351-        code = str(args.get("code") or "")
352-        low = code.lower()
353-        if "manifest.json" in low and any(
354-            marker in low
355-            for marker in ("write_file(", "write_text(", ".write(", "json.dump(", "open(")
356-        ):
357-            return _block(
358:                "Shorts production guard: PROOF_FREEZE — do not mutate manifest.json through "
359-                "execute_code after an accepted proof. Use patch/write_file instead so the runtime "
360-                "can compare the prospective proof fingerprint and allow proof-irrelevant edits "
361-                "while blocking changes to accepted creative proof/casting."
362-            )
363-        return None
364-
365-    raw_path = str(args.get("path") or args.get("file_path") or "")
366-    if not raw_path:
367-        return None
368-    try:
--
407-                return None
408-            prospective_text = (
409-                current_text.replace(old, new)
410-                if args.get("replace_all") is True
411-                else current_text.replace(old, new, 1)
412-            )
413-
414-        prospective = json.loads(prospective_text)
415-        if _runtime_proof_fingerprint(prospective) != current_fp:
416-            return _block(
417:                "Shorts production guard: PROOF_FREEZE — accepted proof is locked. "
418-                "Do not change creative_intent, proof candidates, or selected footage/casting "
419:                "after Vision PASS. Continue with TTS/render/final QA using the accepted plan. "
420:                "A genuine proof failure must be handled before acceptance, not by reopening "
421:                "the proof loop after PASS."
422-            )
423-    except Exception:
424-        return None
425-    return None
426-
427-
428-def _proof_attempt_state_path(image_path: Path, pending: Optional[dict] = None) -> Path:
429-    """Use one attempt ledger per canonical proof receipt, not per proofN workdir."""
430-    if isinstance(pending, dict):
431-        receipt = pending.get("receipt")
--
595-              "include only information needed by the production executor."
596-        )
597-        typed_tasks[0]["output_schema"] = _CREATIVE_CONTRACT_SCHEMA
598-        return {"action": "modify", "args": {"tasks": typed_tasks}}
599-
600-    if not alternate_method and not explicit_system_change:
601-        rediscovery_block = _canonical_rediscovery_block(tool_name, args, session_id)
602-        if rediscovery_block:
603-            return rediscovery_block
604-
605:        freeze_block = _proof_freeze_block(tool_name, args)
606:        if freeze_block:
607:            return freeze_block
608-
609-    if not alternate_method and not explicit_system_change and _direct_render_bypass(tool_name, args):
610-        return _block(
611-            "Shorts production guard: direct TTS/final assembly outside the canonical pipeline "
612-            "is blocked during normal production. Use shorts_fast_pipeline.py. If proof Vision "
613-            "cannot issue a valid receipt, stop with QA_UNAVAILABLE; do not build a fallback final."
614-        )
615-
616-    # Explicitly requested alternate/code-driven workflows remain available.
617-    if tool_name == "skill_view" and not alternate_method:
618-        name = str(args.get("name") or "")
619-        if name and name != "shorts-core":
620-            return _block(
621-                "Shorts production guard: shorts-core is the sole production authority "
622-                "for this footage-based Shorts/Reels turn. Do not load another production, "
623-                "debug, installation, or QA skill. Continue with shorts-core and the canonical pipeline."
624-            )
625-
626-    # Vision policy: do not impose arbitrary call counts.
627-    # The agent may re-evaluate when the evidence or question genuinely changes.
628:    # Only exact no-progress repetition of the same visual evidence + question is blocked.
629-    if tool_name in {"browser_vision", "computer_use"} and not alternate_method:
630-        return _block(
631-            "Shorts production guard: this QA fallback is disabled during normal production. "
632-            "Use vision_analyze as the primary visual QA path; if Vision is unavailable after "
633-            "the permitted retry, continue with controlled QA_UNAVAILABLE rather than inventing a fallback."
634-        )
635-
636-    if tool_name == "vision_analyze":
637-        image_url = str(args.get("image_url") or "")
638-        question = str(args.get("question") or "").strip()
639-        raw = image_url[7:] if image_url.startswith("file://") else image_url
640-        image_path = Path(raw).expanduser()
641-
642-        # ASSET-001 diagnostic:
643-        # Pre-casting source/range/asset Vision inspection is intentionally OPEN.
644-        # No artificial Vision-call cap and no mandatory batching in this diagnostic.
645-        # Use visual inspection only when it helps choose footage that actually fulfills
646:        # the creative contract. Existing duplicate/no-progress and other guards remain active.
647-
648-        if image_path.name == "proof_contact_sheet.jpg":
649-            pending_path = image_path.parent / "proof_pending.json"
650-            current_fingerprint = None
651-            pending = {}
652-            try:
653-                if pending_path.exists():
654-                    pending = json.loads(pending_path.read_text(encoding="utf-8"))
655-                    current_fingerprint = pending.get("fingerprint")
656-            except Exception:
657-                pending = {}
658-            proof_state = _read_proof_attempt_state(image_path, pending)
659-
660-            if (
661-                int(proof_state.get("failed_attempts") or 0) >= 2
662-                and current_fingerprint
663-                and proof_state.get("last_fingerprint") == current_fingerprint
664-            ):
665-                return _block(
666-                    "Shorts production guard: PROOF_STRATEGY_EXHAUSTED — this proof strategy "
667:                    "already failed initial review plus one repair. Do not retry Vision on the "
668-                    "same accepted candidates/casting. Materially change the proof footage or "
669-                    "casting, rerun proof-check, and continue the production autonomously."
670-                )
671-
672:        # Content identity matters more than filename: a repaired final_contact_sheet
673-        # at the same path is new evidence and must be allowed to be judged again.
674-        try:
675-            image_identity = _sha_file(image_path.resolve())
676-        except Exception:
677-            image_identity = str(image_path)
678-
679-        normalized_question = re.sub(r"\s+", " ", question).strip()
680-        sig_raw = json.dumps(
681-            {
682-                "session": session_id,
--
685-            },
686-            ensure_ascii=False,
687-            sort_keys=True,
688-        )
689-        signature = hashlib.sha256(sig_raw.encode("utf-8")).hexdigest()
690-
691-        with _LOCK:
692-            if signature in _VISION_SEEN:
693-                return _block(
694-                    "Shorts production guard: this exact visual evidence has already been "
695:                    "asked the same question. Reuse the existing result. If evidence was repaired "
696-                    "or the verification question genuinely needs correction, change the evidence "
697-                    "or question rather than repeating the same call."
698-                )
699-        return None
700-
701-    # During a production turn the agent may USE the installed toolchain, but may not redesign it.
702-    if not explicit_system_change:
703-        if tool_name == "skill_manage":
704-            return _block(
705-                "Shorts production guard: do not modify skills during a production job. "
--
768-        if not prompt.name.endswith("_vision_question.txt"):
769-            return text
770-        if not prompt.is_file() or prompt.stat().st_size > 128 * 1024:
771-            return text
772-        expanded = prompt.read_text(encoding="utf-8").strip()
773-        return expanded or text
774-    except Exception:
775-        return text
776-
777-
778:def _update_repair_state_from_vision(
779-    image_path: Path,
780-    question: str,
781-    result: Any,
782-    session_id: str,
783-    turn_id: str,
784-    tool_call_id: str,
785-) -> None:
786-    if image_path.name != "final_contact_sheet.jpg":
787-        return
788:    state_path = image_path.parent / "repair_state.json"
789-    if not state_path.exists():
790-        return
791-    try:
792-        state = json.loads(state_path.read_text(encoding="utf-8"))
793-    except Exception:
794-        return
795:    if state.get("state") not in {"repair_attempted", "verification_pending"}:
796-        return
797:    token = str(state.get("repair_token") or "")
798:    if not token or f"HERMES_REPAIR_VERIFY::{token}" not in question:
799-        return
800-    try:
801-        if _sha_file(image_path) != state.get("contact_sheet_sha256"):
802-            return
803-        artifact = Path(str(state.get("artifact") or "")).expanduser().resolve()
804-        if not artifact.exists() or _sha_file(artifact) != state.get("artifact_sha256"):
805-            return
806-    except Exception:
807-        return
808-    outer = _json_object(result)
809-    verdict = _json_object(outer.get("analysis")) if outer and outer.get("success") is True else None
810-    if not verdict:
811-        state["state"] = "unverified"
812-        state["verification_pending"] = False
813-        state["claim_fixed_allowed"] = False
814-    else:
815-        accepted = (
816-            verdict.get("verdict") == "PASS"
817-            and verdict.get("verifier") == "vision_analyze"
818:            and verdict.get("post_repair_semantic_verified") is True
819:            and _normalized_proof_token(verdict.get("repair_token")) == token
820-        )
821-        failed = verdict.get("verdict") == "FAIL"
822-        state["state"] = "verified" if accepted else ("failed" if failed else "unverified")
823-        state["verification_pending"] = False
824-        state["claim_fixed_allowed"] = accepted
825-        state["verification"] = {
826-            "verifier": "vision_analyze",
827-            "verdict": verdict.get("verdict"),
828-            "artifact_sha256": state.get("artifact_sha256"),
829-            "contact_sheet_sha256": state.get("contact_sheet_sha256"),
--
885-    with _LOCK:
886-        _VISION_SEEN.add(signature)
887-
888-    raw_path = image_url[7:] if image_url.startswith("file://") else image_url
889-    try:
890-        image_path = Path(raw_path).expanduser().resolve()
891-    except Exception:
892-        return
893-
894-    contract_question = _expanded_vision_question_for_receipt(image_path, question)
895:    _update_repair_state_from_vision(
896-        image_path, contract_question, result, session_id, turn_id, tool_call_id
897-    )
898-
899-    if image_path.name != "proof_contact_sheet.jpg":
900-        return
901-
902-    pending_path = image_path.parent / "proof_pending.json"
903-    if not pending_path.exists():
904-        return
905-
root@213-238-170-219:~#