Filming a live web app
Never use desktop screen capture on this machine
ffmpeg -f avfoundation -i "2" grabs the display, not a window. It has
leaked private content three separate times in one session:
- Chrome not frontmost → captured VS Code with a session transcript in it
open -a "Google Chrome"→ triggered Mission Control, capturing every open window at once- macOS fullscreen → moved Chrome to its own Space, so the capture showed the other Space (VS Code, Finder, a PowerPoint error dialog)
Fullscreen makes it worse, not better. If a frame is ever captured that contains anything private, delete it immediately and say so.
You cannot force fullscreen yourself: ⌃⌘F sent through the extension does not
reach browser chrome, and requestFullscreen() is rejected as untrusted from a
synthetic click — though it does work when armed on mousedown.
Use the extension's recorder — it is tab-only by construction
gif_creator captures the tab viewport. The desktop physically cannot appear.
gif_creator start_recording
→ drive the page
gif_creator export (download: true, showClickIndicators/ActionLabels/
ProgressBar/Watermark all false, quality 4)
Drive frame capture with wait actions, not screenshot actions. Both
produce a frame; only screenshot returns an image into your context, and a
50-frame burst of those will exhaust a session. wait gives the same frames for
free.
Cap is 50 frames per recording. Export between beats and stitch later.
Then dedupe — a 44-frame export is typically 9–29 distinct states:
prev=None
for f in sorted(glob.glob('q/f*.png')):
h=hashlib.md5(open(f,'rb').read()).hexdigest()
if h!=prev: keep.append(f); prev=h
NEVER motion-interpolate text
minterpolate with mi_mode=mci warps pixels along estimated motion vectors.
Between two frames of different text it produces unreadable ghosted soup — it
will try to morph "OPERATING BUDGET" into "CAPITAL BUDGET". Hard cuts, or a
crossfade ≤0.3s. Nothing else. This shipped once and was caught by the human reviewer, not by
the build.
The honest consequence: you cannot manufacture fluid motion from discrete text states. Frame interpolation only works on continuous motion. If the user wants true 30fps of a chat streaming, they record it themselves with ⌘⇧5 while you drive the page — that is the only route, and it takes two minutes.
Audio contract — non-negotiable
- VO bus +6dB, voice
en-US-AndrewMultilingualNeural - Bed
/Library/Audio/Apple Loops/Apple/01 Hip Hop/Slow Drift Ambient Synth.caf sidechaincompress=threshold=0.015:ratio=8:attack=25:release=450:makeup=1alimiter=limit=0.95- NEVER loudnorm
- Gate: every VO slot mean > −19dB; bed-only gaps < −22dB
That bed is quiet material (−37dB mean at −16dB gain), so it passes the gate by 20dB while being effectively inaudible. Raise it until it is actually present.
Every slot must land inside its window. If a read does not fit, widen the window or shorten the copy — never speed up the read. Over ~2.6 words/sec reads rushed; check it and name the slot.
Azure Speech auth
If your Speech resources have disableLocalAuth=true (common under tenant
policy), keys do not exist. Use Entra:
Authorization: aad#<resourceId>#<aadToken>
resourceId = /subscriptions/<subscription-id>/resourceGroups/<resource-group>/
providers/Microsoft.CognitiveServices/accounts/<speech-resource>
token = az account get-access-token --resource https://cognitiveservices.azure.com
Two cuts, two vocabularies
Internal (SEs reviewing the pipeline) may say RAPP Factory, MVP, skills.
Customer-facing must not. The customer has never heard of RAPP, the Factory, RAPPlication, brainstem, egg, MVP, or prototype. Put a vocabulary gate in the build script that hard-fails on those words in narration and card strings — and know that it cannot see the pixels of captured shots, which is where leakage actually lives. Check frames by eye.
The gate
Watch it. Extract frames and READ them across the whole timeline. A green build is not a watched film — this failed twice in one session, once shipping a smeared unwatchable cut.
Then spawn a separate blind adversarial reviewer with the audience brief. It will find things you cannot, because you know what you intended. Real findings it caught that the builder missed:
- Internal tool identifiers on screen for 57 of 105 seconds
- Narration claiming success over a frame where the agent visibly failed
- 23 seconds held on one identical frame
- The artifact's own heading sliced in half at the scroll edge
- The user's question never visible despite three "ask it for…" lines
- Invented person names on screen 12s before the synthetic-data disclaimer
Loop until it returns PASS with zero blockers. Then report residual defects honestly — a known flaw named is fine, a flaw the customer finds is not.
Related
/cs-agent-live to get the agent presentable before you film it.
Run this — do not improvise
This capability's deterministic implementation is a RAPP single-file agent, linked beside this file as tab_film_agent.py and embedded as the fenced Python below (sha256 c714a305c2330727…; a byte-exact copy is also vaulted in the capsule comment at the end of this file). On a host with sandbox execution, run the linked file directly — if it is missing, write the fence contents verbatim to tab_film_agent.py first:
python3 tab_film_agent.py '{"key": "value"}' # arguments as one JSON object
echo '{"key": "value"}' | python3 tab_film_agent.py # or on stdin
python3 tab_film_agent.py --tool # emit the JSON tool contract
Treat stdout as a tool result. If it reports missing or unresolved inputs, stop and collect them. If it returns steps, execute those steps in order exactly as returned; if it returns instructions, follow them with the supplied inputs. Otherwise use the result verbatim. Do not invent behavior beyond that output. On a host without code execution, treat the Parameters schema and the code below as the exact specification and never paraphrase a step. Never edit inside the generated markers; a converter-equipped host can instead restore the original file checksum-verified with the installed rapp-agent-converter/scripts/toast.py convert SKILL.md --to agent.
"""TabFilm -- Capture a demo film from a live web app safely and cut it to narration. Use for any "film this agent / app / portal" ask. Encodes the capture method that works on a developer laptop, the three ways desktop capture leaks private windows, and the post-processing step that destroys text. Trigger on film, record, showcase video, demo video, capture the screen.
Generated by the rapp skill from tab-film. The RCI capsule at the bottom of this file carries the full original; `toast.py convert` restores it byte-exact."""
import json
import re
import sys
try:
from agents.basic_agent import BasicAgent
except ImportError: # running OUTSIDE a brainstem -- stay executable anyway.
class BasicAgent: # noqa: D101 - minimal stand-in, same contract
def __init__(self, name=None, metadata=None):
if name:
self.name = name
if metadata:
self.metadata = metadata
def perform(self, **kwargs):
return "Not implemented."
def system_context(self):
return None
def to_tool(self):
return {"type": "function", "function": {
"name": self.name,
"description": self.metadata.get("description", ""),
"parameters": self.metadata.get("parameters", {})}}
# The procedural layer, verbatim from the source capability.
INSTRUCTIONS = '# Filming a live web app\n\n## Never use desktop screen capture on this machine\n\n`ffmpeg -f avfoundation -i "2"` grabs **the display**, not a window. It has\nleaked private content three separate times in one session:\n\n- Chrome not frontmost → captured VS Code with a session transcript in it\n- `open -a "Google Chrome"` → triggered Mission Control, capturing every open\n window at once\n- macOS fullscreen → moved Chrome to its own Space, so the capture showed the\n *other* Space (VS Code, Finder, a PowerPoint error dialog)\n\nFullscreen makes it worse, not better. If a frame is ever captured that contains\nanything private, delete it immediately and say so.\n\nYou cannot force fullscreen yourself: `⌃⌘F` sent through the extension does not\nreach browser chrome, and `requestFullscreen()` is rejected as untrusted from a\nsynthetic `click` — though it **does** work when armed on `mousedown`.\n\n## Use the extension's recorder — it is tab-only by construction\n\n`gif_creator` captures the tab viewport. The desktop physically cannot appear.\n\n```\ngif_creator start_recording\n → drive the page\ngif_creator export (download: true, showClickIndicators/ActionLabels/\n ProgressBar/Watermark all false, quality 4)\n```\n\n**Drive frame capture with `wait` actions, not `screenshot` actions.** Both\nproduce a frame; only `screenshot` returns an image into your context, and a\n50-frame burst of those will exhaust a session. `wait` gives the same frames for\nfree.\n\nCap is 50 frames per recording. Export between beats and stitch later.\n\nThen dedupe — a 44-frame export is typically 9–29 distinct states:\n\n```python\nprev=None\nfor f in sorted(glob.glob('q/f*.png')):\n h=hashlib.md5(open(f,'rb').read()).hexdigest()\n if h!=prev: keep.append(f); prev=h\n```\n\n## NEVER motion-interpolate text\n\n`minterpolate` with `mi_mode=mci` warps pixels along estimated motion vectors.\nBetween two frames of *different text* it produces unreadable ghosted soup — it\nwill try to morph "OPERATING BUDGET" into "CAPITAL BUDGET". Hard cuts, or a\ncrossfade ≤0.3s. Nothing else. This shipped once and was caught by the human reviewer, not by\nthe build.\n\nThe honest consequence: you cannot manufacture fluid motion from discrete text\nstates. Frame interpolation only works on continuous motion. If the user wants true\n30fps of a chat streaming, **they record it themselves** with ⌘⇧5 while you drive\nthe page — that is the only route, and it takes two minutes.\n\n## Audio contract — non-negotiable\n\n- VO bus **+6dB**, voice `en-US-AndrewMultilingualNeural`\n- Bed `/Library/Audio/Apple Loops/Apple/01 Hip Hop/Slow Drift Ambient Synth.caf`\n- `sidechaincompress=threshold=0.015:ratio=8:attack=25:release=450:makeup=1`\n- `alimiter=limit=0.95`\n- **NEVER loudnorm**\n- Gate: every VO slot mean > **−19dB**; bed-only gaps < **−22dB**\n\nThat bed is quiet material (−37dB mean at −16dB gain), so it passes the gate by\n20dB while being effectively inaudible. Raise it until it is actually present.\n\n**Every slot must land inside its window.** If a read does not fit, widen the\nwindow or shorten the copy — **never speed up the read**. Over ~2.6 words/sec\nreads rushed; check it and name the slot.\n\n### Azure Speech auth\n\nIf your Speech resources have `disableLocalAuth=true` (common under tenant\npolicy), keys do not exist. Use Entra:\n\n```\nAuthorization: aad#<resourceId>#<aadToken>\nresourceId = /subscriptions/<subscription-id>/resourceGroups/<resource-group>/\n providers/Microsoft.CognitiveServices/accounts/<speech-resource>\ntoken = az account get-access-token --resource https://cognitiveservices.azure.com\n```\n\n## Two cuts, two vocabularies\n\n**Internal** (SEs reviewing the pipeline) may say RAPP Factory, MVP, skills.\n\n**Customer-facing** must not. The customer has never heard of RAPP, the Factory,\nRAPPlication, brainstem, egg, MVP, or prototype. Put a **vocabulary gate in the\nbuild script** that hard-fails on those words in narration and card strings — and\nknow that it cannot see the pixels of captured shots, which is where leakage\nactually lives. Check frames by eye.\n\n## The gate\n\n**Watch it.** Extract frames and READ them across the whole timeline. A green\nbuild is not a watched film — this failed twice in one session, once shipping a\nsmeared unwatchable cut.\n\nThen spawn a **separate blind adversarial reviewer** with the audience brief.\nIt will find things you cannot, because you know what you intended. Real findings\nit caught that the builder missed:\n\n- Internal tool identifiers on screen for 57 of 105 seconds\n- Narration claiming success over a frame where the agent visibly failed\n- 23 seconds held on one identical frame\n- The artifact's own heading sliced in half at the scroll edge\n- The user's question never visible despite three "ask it for…" lines\n- Invented person names on screen 12s before the synthetic-data disclaimer\n\nLoop until it returns PASS with zero blockers. Then report residual defects\nhonestly — a known flaw named is fine, a flaw the customer finds is not.\n\n## Related\n\n`/cs-agent-live` to get the agent presentable before you film it.'
# Ordered commands lifted verbatim from the capability's own documentation.
STEPS = []
class TabFilmAgent(BasicAgent):
def __init__(self):
self.name = 'TabFilm'
self.metadata = {
"name": "TabFilm",
"description": "Capture a demo film from a live web app safely and cut it to narration. Use for any \"film this agent / app / portal\" ask. Encodes the capture method that works on a developer laptop, the three ways desktop capture leaks private windows, and the post-processing step that destroys text. Trigger on film, record, showcase video, demo video, capture the screen.",
"parameters": {
"type": "object",
"properties": {},
"required": []
}
}
super().__init__(name=self.name, metadata=self.metadata)
def perform(self, **kwargs): # toaster:generated-perform
return json.dumps({"status": "ok", "instructions": INSTRUCTIONS,
"inputs": kwargs,
"note": "Prose-only capability: follow INSTRUCTIONS "
"with the given inputs."}, indent=2)
if __name__ == "__main__":
# echo '{"arg": "value"}' | python3 tab_film_agent.py
# python3 tab_film_agent.py '{"arg": "value"}'
# python3 tab_film_agent.py --tool # emit the JSON tool contract
_a = sys.argv[1:]
if _a and _a[0] == "--tool":
print(json.dumps(TabFilmAgent().to_tool(), indent=2))
else:
_raw = _a[0] if _a else (sys.stdin.read().strip() or "{}")
print(TabFilmAgent().perform(**json.loads(_raw)))
# rci-capsule:v1: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