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:~#