OUTPUT #735 | 21086 karakter | 460 satır
========== YENİ KOMUT | 17.09.2026 17:29:01 ==========


========== YENİ KOMUT | 17.09.2026 17:29:01 ==========

/home/hermes/.hermes/skills/media/shorts-core/SKILL.md
151-**KNOWN FAILURE LESSONS — çözüm yöntemi dayatma, problemi çöz**
152-- repeated / same-family footage
153-- uzun generic infographic veya card blokları
154-- cross-video template smell
155-- zayıf shot progression
156-- payoff'ın görsel olarak kapanmaması
157-- gereksiz tool / method churn
158-
159-**CREATIVE AUTHORITY**
160-- konuya özgü hook ve visual thesis
161-- real-footage-first omurga
162-- yaklaşık 6–8 anlamlı shot ile güçlü progression
163:- asset çeşitliliği ve batch asset discovery hedefleri
164-- yalnız görünmeyen mekanizma için kısa grafik/annotation
165-- net, görünür payoff
166-- narration'ın içerik/dramatik akışı
167-
168-Astra çözüm yönteminde yaratıcı olarak serbesttir; yukarıdaki teknik baseline'a müdahale edemez. Web/terminal/browser/TTS/Vision veya production tool kullanmaz. Uzun manifesto, alternatif A/B/C planları, temporary helper, micro-loop veya ikinci re-plan üretmez.
169-
170-Tek çıktı kısa ve uygulanabilir bir contract olsun:
171:`creative_thesis -> hook -> narration_arc -> shot progression -> mechanism -> payoff -> batch_asset_targets -> creative_donts`
172-
173-Bu production request içinde ikinci Astra delegation yapma. Sol, dönen contract'ı yaratıcı yön olarak kullanır ve teknik execution'ı mevcut Golden kurallarıyla kendisi tamamlar.
174-
175-Tek ana-model turunda mümkün olduğunca birlikte çöz:
176-
177-- izleyici vaadi, hook, payoff ve kısa Türkçe narration;
178-- seçilmiş TTS sesi ve hız/pitch değerleri;
179-- VTT zamanlamasını kullanacak altyazı yaklaşımı;
180-- her beat için shot manifesti ve mevcut yerel asset yolu;
181-- final çıktı yolu ve kabul kriterleri.
182-
183-Mevcut workspace ve asset yolları biliniyorsa `/home/hermes` genelinde tekrar `search_files` taraması yapma. Bilinen proje dizinini ve mevcut manifest/cache bilgisini kullan.
184-
185-Production sırasında TTS sesi yeniden seçme veya keşfetme; varsayılan `tr-TR-Chirp3-HD-Algieba` / `0.90` baseline'ını koru.
186-
187:### ASSET-001 — Pre-casting visual inspection diagnostic
188-
189:Bu tanı koşusunda asset/casting kararını yalnız metadata, dosya adı veya arama açıklamasından verme.
190:Gerekli olduğunda aday gerçek footage/source/range/assetleri `vision_analyze` ile gerçekten gör.
191-
192-Değerlendir:
193-- gerçek hareket ve görüntü kalitesi;
194-- shot'ın anlatısal işlevini gerçekten kanıtlayıp kanıtlamadığı;
195-- aynı video içinde adayların görsel olarak birbirini gereksiz tekrar edip etmediği;
196-- kadraj, viewpoint, scale, environment ve motion bakımından yeterli progression olup olmadığı.
197-
198-Güzel fakat anlatısal işlevi karşılamayan adayı reddet ve gerekirse yeni aday ara.
199:Bu tanı koşusunda yapay Vision çağrı limiti yoktur ve batch zorunlu değildir.
200-Aynı kanıtı aynı soruyla gereksiz tekrar inceleme.
201:Bu izin yalnız asset/casting görsel değerlendirmesidir; model/provider, TTS, pipeline,
202-config, servis veya teknik mimariyi değiştirme.
203-
204-### Faz 2 — Deterministic fast pipeline
205-
206-Narration, seçilmiş voice ve shot manifest kesinleşince tek seferlik medya işlemleri için:
207-
208-`/home/hermes/.hermes/skills/media/shorts-core/scripts/shorts_fast_pipeline.py`
209-
210-kullan.
211-
212-Çalıştırıcı:
213-
--
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:
237-
238-- gerçek 9:16 tam ekran;
239-- siyah/boş bant veya yanlış crop;
240-- en fazla iki satır altyazı ve safe area;
241-- Türkçe yazım/karakterler;
242-- gerçek footage önceliği ve shot çeşitliliği;
243-- render bozulması;
244-- anlatım ile görselin semantik uyumu.
245-
246-PASS ise yeniden render veya ikinci contact sheet üretme.

/home/hermes/.hermes/plugins/shorts-production-guard/__init__.py
251-        )
252-    _CANONICAL_READS.add(key)
253-    return None
254-
255-def _block(message: str) -> dict[str, str]:
256-    return {"action": "block", "message": message}
257-
258-
259-def _runtime_proof_fingerprint(data: dict) -> str:
260-    """Mirror the canonical proof fingerprint so accepted proof can be frozen at runtime."""
261-    creative = data.get("creative_intent") or {}
262-    gate = data.get("proof_gate") or {}
263:    candidates = gate.get("candidates") or []
264-
265-    normalized = []
266:    for c in candidates:
267-        if not isinstance(c, dict):
268-            normalized.append(c)
269-            continue
270-        item = dict(c)
271-        src = item.get("src")
272-        if isinstance(src, str) and src.strip():
273-            path = Path(src).expanduser()
274-            if path.exists():
275-                st = path.stat()
276-                item["_source_identity"] = {
277-                    "path": str(path.resolve()),
278-                    "size": st.st_size,
279-                    "mtime_ns": st.st_mtime_ns,
280-                }
281-            else:
282-                item["_source_identity"] = {"path": str(path), "missing": True}
283-        normalized.append(item)
284-
285:    selected_casting = []
286-    for index, shot in enumerate(data.get("shots") or []):
287-        if not isinstance(shot, dict):
288-            continue
289-        kind = str(shot.get("type") or "").lower()
290-        origin = str(shot.get("source_origin") or "").strip().lower()
291-        src_value = shot.get("src")
292-        footage_backed = (
293-            "footage" in kind
294-            or (
295-                shot.get("materialized") is True
296-                and bool(origin)
297-                and not origin.startswith(("generated:", "synthetic:", "ai-generated:"))
--
299-                and Path(src_value).expanduser().suffix.lower() in {".mp4", ".mov", ".mkv", ".webm", ".m4v"}
300-            )
301-        )
302-        if not footage_backed:
303-            continue
304-        item = {
305-            "shot": index,
306-            "src": shot.get("src"),
307-            "source_origin": shot.get("source_origin"),
308-            "visual_goal": shot.get("visual_goal"),
309-            "in": shot.get("in", 0),
310-            "duration": shot.get("duration"),
311:            "casting": shot.get("casting"),
312-        }
313-        src = item.get("src")
314-        if isinstance(src, str) and src.strip():
315-            path = Path(src).expanduser()
316-            if path.exists():
317-                st = path.stat()
318-                item["_source_identity"] = {
319-                    "path": str(path.resolve()),
320-                    "size": st.st_size,
321-                    "mtime_ns": st.st_mtime_ns,
322-                }
323-            else:
324-                item["_source_identity"] = {"path": str(path), "missing": True}
325:        selected_casting.append(item)
326-
327-    payload = {
328-        "creative_intent": {
329-            "promise": creative.get("promise"),
330-            "primary_visual_proof": creative.get("primary_visual_proof"),
331-            "hook_visual_goal": creative.get("hook_visual_goal"),
332-            "payoff": creative.get("payoff"),
333-        },
334-        "proof_gate": {
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:
369-        path = Path(raw_path).expanduser().resolve()
370-    except Exception:
371-        return None
372-    if path.name != "manifest.json" or not path.exists():
373-        return None
--
406-            if count > 1 and args.get("replace_all") is not True:
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):
--
538-def _normalized_proof_token(value: Any) -> str:
539-    """Accept the harmless exact-prefix variation repeatedly emitted by Vision."""
540-    token = str(value or "").strip()
541-    prefix = "HERMES_PROOF_GATE::"
542-    return token[len(prefix):] if token.startswith(prefix) else token
543-
544-
545-def _proof_verdict_accepted(verdict: Any, token: str) -> bool:
546-    """Accept proof only when every selected footage role is unambiguous."""
547-    return bool(
548-        isinstance(verdict, dict)
549-        and verdict.get("verdict") == "PASS"
550:        and verdict.get("verifier") == "vision_analyze"
551-        and verdict.get("primary_visual_proof") is True
552-        and verdict.get("hook_visual_goal") is True
553-        and verdict.get("proof_visible_and_unmistakable") is True
554:        and verdict.get("casting_subjects_clear") is True
555-        and _normalized_proof_token(verdict.get("proof_token")) == token
556-    )
557-
558-
559-def _on_pre_tool_call(
560-    tool_name: str = "",
561-    args: Any = None,
562-    session_id: str = "",
563-    turn_id: str = "",
564-    **_: Any,
565-) -> Optional[dict]:
566-    active, alternate_method, explicit_system_change = _production_context(session_id)
--
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-            {
--
707-            )
708-
709-        if tool_name in {"write_file", "patch"}:
710-            path = str(
711-                args.get("path")
712-                or args.get("file_path")
713-                or ""
714-            )
715-            if path.endswith("proof_receipt.json") or path.endswith("proof_pending.json"):
716-                return _block(
717-                    "Shorts production guard: proof state is runtime-owned. "
718-                    "Do not write or patch proof receipts/pending attestations manually; "
719:                    "only a real vision_analyze PASS may issue the receipt."
720-                )
721-            if any(path.startswith(prefix) for prefix in _PROTECTED_PREFIXES):
722-                return _block(
723-                    "Shorts production guard: production-time mutation of Hermes core/tools/skills "
724-                    "is blocked. Use the installed production stack as-is and finish the video."
725-                )
726-
727-        if tool_name == "terminal":
728-            cmd = str(args.get("command") or "")
729-            low = cmd.lower()
730-            if "proof_receipt.json" in low or "proof_pending.json" in low:
731-                return _block(
--
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"),
830-            "session_id": session_id,
831-            "turn_id": turn_id,
832-            "tool_call_id": tool_call_id,
833-            "accepted_at": time.time(),
834-            "raw": verdict,
835-            "vision_route": outer.get("vision_route") if isinstance(outer.get("vision_route"), dict) else None,
836-        }
837-    try:
838-        tmp = state_path.with_suffix(state_path.suffix + ".tmp")
--
843-
844-
845-def _on_post_tool_call(
846-    tool_name: str = "",
847-    args: Any = None,
848-    result: Any = None,
849-    session_id: str = "",
850-    turn_id: str = "",
851-    tool_call_id: str = "",
852-    status: str = "",
853-    **_: Any,
854-) -> None:
855:    if tool_name != "vision_analyze":
856-        return
857-
858-    active, _, _ = _production_context(session_id)
859-    if not active:
860-        return
861-    args = args if isinstance(args, dict) else {}
862-    image_url = str(args.get("image_url") or "")
863-    question = str(args.get("question") or "")
864-    if not image_url:
865-        return
866-
867-    # A provider failure is not reviewed evidence. Mark only completed Vision
--
947-    state["accepted"] = True
948-    state["last_verdict"] = "PASS"
949-    state["last_fingerprint"] = pending.get("fingerprint")
950-    state["updated_at"] = time.time()
951-    _write_proof_attempt_state(image_path, state, pending)
952-
953-    try:
954-        receipt = Path(str(pending["receipt"])).expanduser().resolve()
955-        receipt.parent.mkdir(parents=True, exist_ok=True)
956-        obj = {
957-            "pass": True,
958-            "receipt_source": "shorts-production-guard/post_tool_call",
959:            "verifier": "vision_analyze",
960-            "fingerprint": pending["fingerprint"],
961-            "contact_sheet": str(image_path),
962-            "contact_sheet_sha256": pending["contact_sheet_sha256"],
963-            "verdict": verdict,
964-            "vision_route": outer.get("vision_route") if isinstance(outer.get("vision_route"), dict) else None,
965-            "session_id": session_id,
966-            "turn_id": turn_id,
967-            "tool_call_id": tool_call_id,
968-            "accepted_at": time.time(),
969-        }
970-        tmp = receipt.with_suffix(receipt.suffix + ".tmp")
971-        tmp.write_text(json.dumps(obj, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
root@213-238-170-219:~#