@officexapp/vidfarm-devcli 0.21.58 → 0.21.59

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -28,6 +28,15 @@ checks:
28
28
  - learn more
29
29
  # the bait lives in the POST caption, never on the meme:
30
30
  - comment below
31
+ # Rule 10 — context debt. Each of these is an admission that the caption needs
32
+ # something the viewer does not have on the frame. See "The four contexts a
33
+ # viewer does not have". Banned here as literal substrings so gate 1 catches
34
+ # them before a render exists:
35
+ - iykyk
36
+ - if you know you know
37
+ - you already know
38
+ - as promised
39
+ - part 2
31
40
  ---
32
41
 
33
42
  # Meme Recaption
@@ -63,14 +72,54 @@ Three questions. **All three must be yes** or you have not got a meme recaption
63
72
 
64
73
  | # | Question | If no |
65
74
  |---|---|---|
66
- | 1 | **Standalone** — Show it to someone who has never heard of the offer. Do they laugh? | You wrote an ad with a meme stapled on |
75
+ | 1 | **Standalone** — Show it to someone who has never heard of the offer, and who did not see your last post, and who has never seen the source meme. Do they laugh? | You wrote something that needs a briefing. See Rule 10 |
67
76
  | 2 | **Problem space** — Is the experience it names one the product actually removes? | You wrote a meme. Funny, worthless. Post it from a personal account |
68
- | 3 | **Enactment** — Is the meme's *physical action* the literal body of the feeling the caption names? | You wrote a caption over stock footage |
77
+ | 3 | **Enactment** — Does the clip *enact* the caption — same situation, told twice, once in a body and once in words? Run the two covers below. **Not "does it suit the vibe"** | You wrote a caption over stock footage. See Rule 3 |
69
78
 
70
- Test 3 is the one editors skip, and it is the one that separates a meme that lands from a caption
71
- sitting on top of a clip. The meme is not an illustration of the caption. **The meme is what the
72
- caption's subject is doing with their body.** If the caption says "quietly giving up" and the clip
73
- is someone dancing, the joke has no floor, and no amount of caption polish will give it one.
79
+ > **Amended.** Test 3 used to read *"is the meme's physical action the literal body of the feeling
80
+ > the caption names?"* That is still what it means, but it was a single look, and a single look
81
+ > cannot fail see the read-order paragraph below. It is now two covers and a comparison, and the
82
+ > word *literal* has been pinned down: **literal about the VERB, never about the NOUNS.** A caption
83
+ > about 10 tabs enacted by someone staring at 10 tabs is a stock photo, not a meme.
84
+
85
+ ### The two-cover test — the enactment test, made testable
86
+
87
+ You cannot check a match by looking at the finished frame, because by the time you look at it you
88
+ already know the answer. So you take the two channels apart, describe each one **cold and in
89
+ writing**, and then compare the two sentences you wrote — not the video.
90
+
91
+ | Step | What you do | What you write down | If it fails |
92
+ |---|---|---|---|
93
+ | **Cover the caption** | Watch the clip alone, muted, with the caption hidden | `clip_reads_blind:` one sentence — what is happening to that person, named by a stranger | The clip is a mood, not an action. **Recast.** No caption rescues a clip that does nothing |
94
+ | **Cover the visual** | Read the caption alone, cold, with no clip at all | `caption_reads_blind:` one sentence — the situation a stranger holds after one read | The caption is a stage direction waiting for a picture. **Rewrite.** The caption must already be a complete joke |
95
+ | **Uncover both** | Read your two sentences side by side | — | **They are two different situations.** This is the defect. Change one of them |
96
+
97
+ Both sentences go in the composition, in the enactment declaration, and gate 2 reads them. See
98
+ "Declare the enactment" under Casting.
99
+
100
+ #### The failure mode, and why it is invisible to you
101
+
102
+ The defect this catches has a name: **vibe-adjacency** — a clip that is emotionally *compatible*
103
+ with the caption without being the same event. A caption about quietly giving up, over a clip of
104
+ someone rolling their eyes: both are "annoyed", both are the right *temperature*, and neither is
105
+ the other one. It is the most common ship-blocking defect in this format, and it is the one the
106
+ author is structurally unable to see.
107
+
108
+ *(Its opposite twin is worth naming here too: a clip that is the same event and also the same
109
+ picture — the caption about 10 tabs over someone glaring at 10 tabs. That one is not adjacent, it
110
+ is redundant, and it is Rule 3.3. Both are failures of the same knob, turned the two different
111
+ ways.)*
112
+
113
+ > **The reason is read order.** You wrote the caption first. So you approach the clip already
114
+ > holding the situation in your head, and you see the clip *as* that situation. Any clip that does
115
+ > not actively contradict the caption looks like a perfect match to you, and only to you. The
116
+ > viewer has no such pre-load: they get 1.2 seconds and two channels, and if the channels are only
117
+ > adjacent, the recognition never fires. They do not think "that's a mismatch" — they feel nothing
118
+ > and keep scrolling, which is why this defect never shows up as a complaint, only as a flat
119
+ > number.
120
+
121
+ That is why the test is written as two covers and a comparison rather than as one look. The two
122
+ covers force you to meet each channel without the other one loading the answer for you.
74
123
 
75
124
  ---
76
125
 
@@ -265,11 +314,111 @@ feel specific when you write them, and only one survives being read by a strange
265
314
  > This test is also **elegance-test question 5** (Rule 1). It is listed twice on purpose: it decides
266
315
  > whether the caption works at all, *and* whether the offer can be named inside it.
267
316
 
317
+ ### The four contexts a viewer does not have
318
+
319
+ The cold-viewer test above covers **one** source of missing context — the product. There are three
320
+ more, and they kill a caption just as dead. The rule underneath all four is Rule 10:
321
+
322
+ > **Every word the caption needs must already be on the frame or already in the viewer's life.
323
+ > Nothing may be borrowed from off-screen.**
324
+
325
+ | # | The context they do not have | What it looks like when you lean on it | Why it is fatal |
326
+ |---|---|---|---|
327
+ | 1 | **The product** | brand names, feature words, category jargon, the founder's demo | Lands only on people who already bought — which defeats a meme ad. *(the existing cold-viewer test)* |
328
+ | 2 | **Your previous post** | `part 2`, `as promised`, `like i said`, a callback to another video | The platform serves this video cold, on purpose. Most people who see video 2 never saw video 1, and the ones who did are the ones you already have |
329
+ | 3 | **The source meme's own lore** | `for 47 seconds`, `and my day is ruined`, naming the character, riffing on what the meme originally meant | Caps your reach at **the meme's fandom**, which is not your niche. The clip is borrowed *equipment*, not a shared reference. Checked by `caption_does_not_borrow_lore` |
330
+ | 4 | **Your own life** | a detail only you can verify — the specific street, the specific colleague, the actual date | This is **rung 4** on the ladder above, arriving by a different road. It is your story, not theirs |
331
+
332
+ **And the phrase that admits all four at once: `iykyk`.** *"If you know, you know"* is a caption
333
+ telling you it did not do its job, while explicitly excluding everyone you needed to reach. It is
334
+ banned in the front matter, so gate 1 stops it before a render exists.
335
+
336
+ **The mechanical version:** hand the caption, as plain text, to someone who has seen none of your
337
+ posts, does not know the product, and has never seen the clip. If they need one question answered
338
+ before they get it, the caption is not finished. **A cold viewer with zero setup gets it, or it
339
+ does not ship.**
340
+
268
341
  **Get the specifics from the customer, not from your imagination.** Their testimonials, their
269
342
  support inbox and their landing-page copy are full of rung-3 sentences written by actual users. In
270
343
  the worked example in Appendix D, the shipped caption is a lightly compressed version of a
271
344
  testimonial printed on the customer's own homepage — which also happens to satisfy Rule 6.
272
345
 
346
+ ### Strictness vs relatability — the two rules above do NOT mean "play it safe"
347
+
348
+ Read Rule 3 and Rule 10 naively and you get this:
349
+
350
+ > ❌ `me when my code doesn't work`
351
+
352
+ It passes both. It is enacted by literally any frowning clip. It needs no context at all. It is
353
+ also completely dead, and if this file let you ship it, the file would be worse than useless. So
354
+ this section is not optional colour — it is the half of the format the strict rules do not carry.
355
+
356
+ **The diagnosis:** that caption is standalone *because* it is universal, and universal is the same
357
+ thing as unfelt. The ladder already says so at rung 1: *"true of everyone, felt by no one."*
358
+ Tightening for context and tightening for specificity feel like the same move — they are opposites.
359
+
360
+ #### The one distinction that resolves it: context vs detail
361
+
362
+ > **Context is what the viewer must be TOLD. Detail is what the viewer already HAS.
363
+ > Rule 10 turns context to zero. The ladder turns detail as high as it goes. They are two dials.**
364
+
365
+ A safe, dead caption is what you get by turning both down at once — which is the default mistake,
366
+ because at 2am both feel like "things I'm assuming the reader knows".
367
+
368
+ **The test that separates them, one question per specific in your caption:**
369
+
370
+ > **Who had to have been there?**
371
+
372
+ | The specific | Who had to have been there | Verdict |
373
+ |---|---|---|
374
+ | `10 tabs` | anyone who has ever tried to pick dinner | **Detail.** Keep it. Add more |
375
+ | `since 7pm` | anyone with an evening | **Detail** |
376
+ | `one scallop roll` | anyone who has wanted one exact thing | **Detail** — and it is the best word in the caption |
377
+ | `the v2 sync` | someone who bought the product | **Context.** Cut |
378
+ | `part 2` | someone who saw your last post | **Context.** Cut |
379
+ | `for 47 seconds` | someone who has seen the source meme | **Context.** Cut |
380
+ | `that place on ludlow` | you | **Context.** Cut — this is rung 4 |
381
+
382
+ Detail is *shared* and specific. Context is *private* and specific. They look identical while you
383
+ are typing, which is exactly why you write the question down and answer it per word.
384
+
385
+ #### Strictness makes the caption MORE relatable, in two mechanical ways
386
+
387
+ Not as a consolation. As arithmetic:
388
+
389
+ 1. **The word budget.** A caption is 8–20 words and it spends every one of them on either context
390
+ or detail. Every word you win back by cutting context is a word you can spend on detail —
391
+ **so spend it.** Cutting `on resy` and not replacing it leaves a weaker caption; cutting it and
392
+ buying `since 7pm` leaves a stronger one. **Deleting context without re-spending the refund is
393
+ how a correct edit produces a worse video**, and it is the most common way this section gets
394
+ misread.
395
+ 2. **The cover-the-caption step will not accept a flat line.** You have to write down, in one
396
+ sentence, the situation a stranger holds after reading the caption cold. *"A person is sad"* is
397
+ not a situation and will not survive being compared to a clip's blind read — the two sentences
398
+ have nothing to agree on. **The test that looks like it would flatten the caption is the test
399
+ that refuses a flat one.**
400
+
401
+ #### And the same distinction on the visual side
402
+
403
+ > **Enactment is about the VERB, not the WORLD. The verb must match; the world must not.**
404
+
405
+ - The clip must **do** the caption's verb — deflate, blank, loop, stare.
406
+ - The clip must **not depict** the caption's nouns. Ten tabs, a laptop, a restaurant, a
407
+ spreadsheet: if the clip shows the thing the caption is about, you have filmed a re-enactment,
408
+ and a re-enactment is a stock photo with a joke written under it.
409
+
410
+ The comedy lives in the gap between where the feeling came from (10 tabs at 7pm) and the body it
411
+ arrives in (a hungover dog). That gap is the same one "Match the INTENSITY" is talking about, and
412
+ it is why `caption_does_not_borrow_lore` fails a caption that reuses a word from the clip's slug:
413
+ a caption that names what the clip literally *is* has closed the gap.
414
+
415
+ #### If you cut the context and the caption goes generic
416
+
417
+ Then you never had a caption — you had a reference. **Go back to Rule 6.** A sentence a real user
418
+ actually said is reliably both context-free and specific, because they said it to a stranger who
419
+ also had none of the context. That is why testimonials outperform anything you would invent, and
420
+ it is the cheapest route out of a caption that got safe.
421
+
273
422
  ### Banned captions
274
423
 
275
424
  - **The product as the thing that WORKS.** The name may appear as a subject in the setup (Rule 1,
@@ -283,6 +432,15 @@ testimonial printed on the customer's own homepage — which also happens to sat
283
432
  - Two-part setups split across a cut. That is a different format and it is weaker.
284
433
  - Anything naming a real competitor doing something bad. Personify a *category* ("every delivery
285
434
  app"), not a company you can be sued by.
435
+ - **Anything with context debt** (Rule 10): `iykyk`, `if you know you know`, `you already know`,
436
+ `part 2`, `as promised`, `like i said`, `see my last`. Each one asks the viewer for something
437
+ they do not have. Banned in the front matter and checked by `caption_no_context_debt`.
438
+ - **Anything riffing on the source meme's own lore.** The clip is borrowed equipment, not a shared
439
+ reference. If the caption only works for people who recognise the meme, its ceiling is that
440
+ meme's fandom. Checked by `caption_does_not_borrow_lore`.
441
+ - **Anything that narrates the clip.** `me quietly giving up` over a clip of someone quietly giving
442
+ up is a subtitle, not a joke — the caption's job is to supply the *situation* the body is
443
+ reacting to, and a clip that already contains the situation leaves it nothing to do.
286
444
 
287
445
  ---
288
446
 
@@ -321,20 +479,45 @@ vidfarm public-raws --category lifestyle --limit 200 # also almost entirely Me
321
479
  `text-graphics` all come back as greenscreen memes. Good news for this format, bad news for
322
480
  backgrounds (see below).
323
481
 
482
+ > **Delivery work: open your delivery log in the same breath as the shelf.** Anything you already
483
+ > sent this client is out (Rule 11), and anything that has been passed on twice is out for everybody.
484
+ > **A clip you have already used is not a shortcut, it is the shelf you did not read** — and the
485
+ > shelf is 851 clips deep, so "there was nothing else" is almost never the real reason.
486
+ >
487
+ > ```bash
488
+ > cut -f1,5,8 CLIPPER/DELIVERED.tsv # client · clip · verdict — read it BEFORE the contact sheet
489
+ > ```
490
+ >
491
+ > Reusing a clip that has *earned* it, on a different client, is correct and encouraged. See
492
+ > *Across clients* under Rule 11.
493
+
324
494
  ### The enactment test, in practice
325
495
 
326
496
  You are casting for a **verb**, not a mood. Write down the verb your caption's subject is doing
327
- with their body, then find the slug that does that verb:
497
+ with their body, then find the slug that does that verb.
328
498
 
329
- | The caption's verb | Slugs that enact it |
330
- |---|---|
331
- | deflating / accepting a bad outcome | `my-disappointment-is-immeasurable-and-my-day-is-ruined`, `jonah-hill-sigh`, `girl-eyerolls` |
332
- | not understanding at all | `huh-cat`, `hangover-dog`, `zoolander-staring`, `nervous` |
333
- | the same thought on a loop | `my-brain-every-10-seconds`, `monkey-cymbal-jolly-chimp`, `zoning-out` |
334
- | smug, having solved it | `gigachad`, `chad-head-bopping`, `check-out-the-big-brain-on-brad` |
335
- | overreacting to something tiny | `neon-screaming`, `jontron-nooooooo`, `girl-fake-crying` |
336
- | telling you you're wrong | `jonathan-frakes-telling-you-youre-wrong-for-47-seconds`, `women-mocking` |
337
- | unbothered while it burns | `my-favorite-thing-in-the-world-to-do-is-nothing`, `boss-baby-chilling` |
499
+ **The verb comes from a closed list of ten tokens.** Not because ten is the number of human
500
+ feelings, but because a free-text verb is not comparable to anything — and the whole point of the
501
+ enactment test is a comparison. A closed token makes "does the clip do what the caption does" a
502
+ string equality instead of a judgement, which is the only form of it a gate can enforce:
503
+
504
+ | Verb token | The body doing it | Slugs that enact it |
505
+ |---|---|---|
506
+ | `deflate` | shoulders drop, eyes go flat, the bad outcome is accepted without a fight | `my-disappointment-is-immeasurable-and-my-day-is-ruined`, `jonah-hill-sigh`, `girl-eyerolls` |
507
+ | `blank` | total non-comprehension — head tilt, dead eyes, nothing processing | `huh-cat`, `hangover-dog`, `zoolander-staring`, `nervous` |
508
+ | `loop` | the same thought going round again, mechanically, forever | `my-brain-every-10-seconds`, `monkey-cymbal-jolly-chimp`, `zoning-out` |
509
+ | `smug` | arms folded, already solved it, insufferable about it | `gigachad`, `chad-head-bopping`, `check-out-the-big-brain-on-brad` |
510
+ | `overreact` | a full-body scream, sob or flail at something trivial | `neon-screaming`, `jontron-nooooooo`, `girl-fake-crying` |
511
+ | `correct` | turning to camera to tell you, patiently, that you are wrong | `jonathan-frakes-telling-you-youre-wrong-for-47-seconds`, `women-mocking` |
512
+ | `unbothered` | the room is on fire and the body has not moved | `my-favorite-thing-in-the-world-to-do-is-nothing`, `boss-baby-chilling` |
513
+ | `celebrate` | arms up, jumping, confetti-scale joy | *the natural win-pole verb — cast it off the shelf and confirm on the sheet* |
514
+ | `stare` | holding eye contact with the camera and saying nothing | *the `<Entity> watching me …` verb (Rule 1, tier 1)* |
515
+ | `wait` | still present, still waiting, visibly longer than is reasonable | *for captions whose collapse is elapsed time* |
516
+
517
+ The last three have no verified slug in this file — nobody has cast them here yet. Find one on the
518
+ shelf and confirm it on the contact sheet; **do not add an eleventh token** to avoid the work of
519
+ choosing among ten. If a clip genuinely does something none of the ten covers, that is a real
520
+ finding: add the token here, with its body line, before you use it.
338
521
 
339
522
  **Then confirm by eye.** Build a contact sheet before you commit — a slug promises an action the
340
523
  clip does not always deliver, and half these clips are 16:9 with the subject small at the bottom:
@@ -343,6 +526,63 @@ clip does not always deliver, and half these clips are 16:9 with the subject sma
343
526
  ffmpeg -v error -i meme.mp4 -vf "fps=1,scale=240:-1,tile=7x1" -frames:v 1 sheet.png
344
527
  ```
345
528
 
529
+ ### Declare the enactment — in the composition, where the gate can read it
530
+
531
+ The two-cover test is a human act. What a script can enforce is that **you actually performed it**
532
+ and that the answers are self-consistent. So the answers live in `composition.html`, at the top,
533
+ above the first layer:
534
+
535
+ ```html
536
+ <!-- meme-recaption: enactment
537
+ clip_slug: my-disappointment-is-immeasurable-and-my-day-is-ruined
538
+ caption_verb: deflate
539
+ clip_verb: deflate
540
+ feeling_size: small
541
+ clip_size: large
542
+ clip_reads_blind: a man in a suit goes flat and accepts something without arguing
543
+ caption_reads_blind: someone gives up on dinner after an hour and settles for pizza
544
+ -->
545
+ ```
546
+
547
+ **It is an HTML comment, not a layer, on purpose.** A text layer would count against
548
+ `max_text_cards`, would need a `data-start`, and — worst — could render. This must never reach a
549
+ pixel. It is build metadata that happens to live in the file the gate already reads, so there is
550
+ no second file to lose.
551
+
552
+ | Field | Rule | Why the gate can hold you to it |
553
+ |---|---|---|
554
+ | `clip_slug` | the raw's slug, as cast | Feeds `caption_does_not_borrow_lore` — the caption may not reuse a word from it |
555
+ | `caption_verb` / `clip_verb` | both from the ten tokens, and **equal** | This is Test 3 as a string equality. Two different tokens is a declared mismatch and the build stops |
556
+ | `feeling_size` / `clip_size` | `small` or `large`, and **different** | "Match the INTENSITY" made enforceable — see below |
557
+ | `clip_reads_blind` | ≥5 words, written while the caption was covered, and it may **not reuse a content word from the caption** | The cover-the-caption step, on the record — and the one field the gate can genuinely interrogate |
558
+ | `caption_reads_blind` | ≥5 words, written while the clip was covered, and not the same sentence as `clip_reads_blind` | The cover-the-visual step, on the record |
559
+
560
+ #### Why `clip_reads_blind` may not borrow the caption's words
561
+
562
+ This is the check with actual teeth, and the reason is the read-order problem again:
563
+
564
+ > **If the only way you can describe the clip is with the caption's own nouns, one of two things is
565
+ > true, and both are fatal.** Either you never saw the clip cold — you projected the caption onto
566
+ > it, which is exactly the blindness the two covers exist to break — or the clip really does depict
567
+ > the caption's nouns, which is a re-enactment and fails Rule 3.3.
568
+
569
+ A correct pair reads with almost no vocabulary in common, because the two channels are *supposed*
570
+ to be different worlds:
571
+
572
+ - ✅ caption `when i've had 10 tabs open since 7pm for one scallop roll and we're getting pizza again`
573
+ · clip `a man in a suit goes flat and accepts something without arguing` — nothing shared, and
574
+ they are obviously one situation
575
+ - ❌ clip `a guy who has had 10 tabs open gives up` — you wrote the caption again with "a guy" in
576
+ front of it. `clip_read_is_independent` fails
577
+
578
+ > **What no gate can check, and this file will not claim otherwise:** whether the two sentences
579
+ > describe the *same* situation. Lexical overlap does not measure it — an early draft of this
580
+ > section required the two blind reads to share a word, and a correct pair shares none, so the
581
+ > check would have fired on every good build and been switched off within a day. **The comparison
582
+ > is gate 3. It always was.** What the declaration buys is that the comparison becomes a discrete
583
+ > act with a written output, performed before you have seen the finished frame — not a feeling you
584
+ > had at 2am with both channels already loaded in your head.
585
+
346
586
  ### Match the INTENSITY, not just the verb
347
587
 
348
588
  The enactment test gets you the right action. This gets you the right *size* of it — and it is
@@ -357,6 +597,17 @@ where the comedy actually comes from.
357
597
  - Matching a big feeling to a big reaction is not a joke, it is a re-enactment. Matching a small
358
598
  feeling to a small reaction is not anything at all.
359
599
 
600
+ **Declare both sizes and they must differ.** `feeling_size` and `clip_size` in the enactment block
601
+ are `small` or `large`, and `intensity_mismatched` fails when they are equal. Two coarse buckets,
602
+ not a scale, because a scale invites a 3-vs-4 that means nothing; the question the format actually
603
+ asks is only ever *"is the reaction the wrong size for the cause?"* — and the answer to that is
604
+ binary.
605
+
606
+ > **This does not contradict "the verb must match".** The verb is *what* the body does; the size is
607
+ > *how much*. Enactment requires the same verb; comedy requires a different amount of it. A caption
608
+ > about a mildly annoying evening (`small`) enacted by a man whose day is immeasurably ruined
609
+ > (`large`) is both rules satisfied at once — same verb, wrong size — and it is the worked example.
610
+
360
611
  **Choose the pain or win that the clip's energy already fits, rather than fighting the footage.**
361
612
  You have 851 clips and one caption you have not written yet; it is far cheaper to pick the feeling
362
613
  that suits a great clip than to hunt for a clip that suits a feeling you are attached to. When a
@@ -713,10 +964,14 @@ are checked (`offer_not_in_payoff`, `caption_does_not_resolve`).
713
964
  2. Is it still **≤20 words** with the offer in it?
714
965
  3. Does the offer read as a **character**, or as a sponsor who bought a slot?
715
966
  4. Does the **collapse still land on the person**?
716
- 5. Would it still be funny to someone who has **never heard of the offer**?
967
+ 5. Would it still be funny to someone who has **never heard of the offer**, **did not see your last
968
+ post**, and has **never seen the source meme**? (Rule 10 — all four contexts, not just the
969
+ product)
717
970
 
718
971
  Question 5 is the one that catches most failures. If the joke needs the reader to know what the
719
972
  product does, the product is not a character — it is an explanation, and explanations are not funny.
973
+ The same is true of the other three borrowed contexts: each one turns the caption into something
974
+ that has to be introduced, and nobody introduces a meme.
720
975
 
721
976
  #### The handle (tier 2), specced
722
977
 
@@ -763,10 +1018,32 @@ the last clause; on the win pole it may be in both, and the discipline moves to
763
1018
  of it. **If you did not decide the pole, you are on the pain pole** — it is the safer default and the
764
1019
  one that travels.
765
1020
 
766
- ### Rule 3 — cast for the verb, not the mood
1021
+ ### Rule 3 — the clip ENACTS the caption. Same verb, wrong size, different world
767
1022
 
768
- Written above as the enactment test; repeated here because it is the rule most often lost between
769
- casting and the timeline. Name the physical action in the build log before you open the shelf.
1023
+ Written above as the two-cover test; repeated here because it is the rule most often lost between
1024
+ casting and the timeline, and because "cast for the verb" alone was too easy to satisfy.
1025
+
1026
+ > **Amended.** Rule 3 used to read *"cast for the verb, not the mood"*, and that is still the first
1027
+ > half of it. It was not enough: a verb named only in the build log is a verb nobody compares, so
1028
+ > the rule passed every time it was read. Rule 3 is now three claims, all of which are declared in
1029
+ > the composition and two of which the gate can fail you on.
1030
+
1031
+ 1. **Same verb.** `caption_verb` and `clip_verb` are the same token from the closed list of ten.
1032
+ Not similar. The same. — `enactment_verbs_match`
1033
+ 2. **Wrong size.** `feeling_size` ≠ `clip_size`. The mismatch between the size of the cause and the
1034
+ size of the reaction is where the comedy actually is. — `intensity_mismatched`
1035
+ 3. **Different world.** The clip does the caption's verb; it must not depict the caption's nouns. A
1036
+ clip that shows the thing the caption is about is a re-enactment, and the gap is the joke. —
1037
+ partially caught by `caption_does_not_borrow_lore`
1038
+
1039
+ **And the test you run before any of that:** cover the caption, cover the visual, write one
1040
+ sentence for each, compare the sentences. The failure it exists for is vibe-adjacency, and the
1041
+ reason it is written as two covers is that you read the caption first and can no longer see the
1042
+ clip cold. Both sentences go in the composition, and `clip_reads_blind` may not reuse a content
1043
+ word from the caption. — `enactment_declared`, `clip_read_is_independent`
1044
+
1045
+ **The comparison itself is yours.** No script decides whether two sentences name one situation.
1046
+ The gate makes sure you wrote them, wrote them cold, and did not write the caption twice.
770
1047
 
771
1048
  ### Rule 4 — the action lands AFTER the caption is read, and frame 0 has a person on it
772
1049
 
@@ -825,6 +1102,103 @@ same complaint — so ask for exactly that.
825
1102
  > Post caption pattern: `<the caption's situation, restated in one line>. <one question inviting
826
1103
  > their version>`
827
1104
 
1105
+ ### Rule 10 — the caption stands alone. Zero setup, zero borrowed context
1106
+
1107
+ **A cold viewer with nothing gets it, or it does not ship.** Nothing they see was explained
1108
+ anywhere else, because nowhere else is reachable: the platform hands them one muted 7-second video
1109
+ between two strangers' videos, and everything the joke needs has to already be on that frame or
1110
+ already in their life.
1111
+
1112
+ Four things you may not borrow — the product, your previous post, the source meme's lore, and your
1113
+ own private history. They are set out with their reasons under "The four contexts a viewer does not
1114
+ have". `iykyk` is banned outright, in the front matter, because it is the caption admitting it
1115
+ failed while excluding the people you needed.
1116
+
1117
+ **Rule 10 is not a licence to go generic, and read carelessly it produces exactly that.** Context
1118
+ and detail are two separate dials: Rule 10 turns *context* to zero, the specificity ladder turns
1119
+ *detail* to maximum, and the caption gets more relatable, not less, as long as you **re-spend the
1120
+ words you win back.** Cutting `on resy` and stopping there leaves a weaker caption; cutting it and
1121
+ buying `since 7pm` leaves a stronger one. The one question that sorts a word into the right dial is
1122
+ **"who had to have been there?"** — see "Strictness vs relatability", which is the load-bearing
1123
+ half of this rule.
1124
+
1125
+ Checked by `caption_no_context_debt` and `caption_does_not_borrow_lore`, and by the front matter's
1126
+ `forbid_text` list at gate 1.
1127
+
1128
+ ### Rule 11 — one client never gets the same meme, or the same joke, twice
1129
+
1130
+ **This rule only applies when you are delivering to somebody** — a marketplace buyer, a retainer
1131
+ client, a customer's own account. On an account you own, ignore it and run the matrix.
1132
+
1133
+ > **No two videos delivered to the same client may share a `clip_slug`, a caption frame, or a pain —
1134
+ > unless that client asked for a series, in writing.**
1135
+
1136
+ Three separate keys, because this format repeats in three separate ways and only the first one is
1137
+ obvious:
1138
+
1139
+ | Key | The repeat | Why it is fatal here specifically |
1140
+ |---|---|---|
1141
+ | `clip_slug` | the same borrowed clip twice | It is not *similar* footage, it is the **same footage**. The buyer recognises it instantly, and the second video looks like the first one with the words changed — which is exactly what it is |
1142
+ | the caption frame | `When I …` twice, or `<Entity> when they …` twice | There are only **eight**, so two pains in one frame land as one joke. The words differ and the shape does not, and the shape is what a viewer remembers |
1143
+ | the pain | the same complaint restaged | You sold one insight three times. This is the one the buyer notices last and resents most |
1144
+
1145
+ **The rights tier compounds it.** 🟢 is the default for client work, which is a small shelf, so a
1146
+ clipper working one buyer for a month runs out of safe clips faster than they expect and starts
1147
+ reaching for one they already used. Cast against your delivery log, not against your memory. When
1148
+ the 🟢 shelf really is exhausted for that client, that is a signal to change the *pain* and cast
1149
+ fresh — not to re-run a clip.
1150
+
1151
+ **Your style repeats. Your joke does not.** The caption plate, the font, the 62% band, the grade on
1152
+ the background, the handle — that is the visual identity the client hired and it should be identical
1153
+ across every delivery. Rule 11 is about the two ingredients at the top of this file: the meme and
1154
+ the caption. Nothing else.
1155
+
1156
+ **The exception, and how narrow it is.** A client who asked for a series, a variant set, or "five
1157
+ cuts of this one joke" has asked for controlled repetition, and then repeating is the deliverable.
1158
+ It must be **written** — in the task payload, in the gig terms, or in their feedback on a proof they
1159
+ kept (`"More like this one."` counts). **An approval is not an ask.** A meme that worked is a reason
1160
+ to write another meme, not a reason to send the same one again.
1161
+
1162
+ #### Across clients — reuse a great raw, retire a mid one
1163
+
1164
+ Rule 11 is a hard rule *within* one client. Across clients it relaxes into a preference, and the
1165
+ thing that decides it is **not novelty, it is how the raw has actually performed.**
1166
+
1167
+ > **A raw that lands is an asset. Carry it to the next buyer. A raw that was only ever okay gets one
1168
+ > outing, and then it is done.**
1169
+
1170
+ Refusing to reuse a proven clip in the name of variety throws away the only edge casting gives you.
1171
+ Five buyers who each see `hangover-dog` once is five good videos. The failure is the other one, and
1172
+ it is specific: **one mediocre clip appearing in most of your recent deliveries**, because it was
1173
+ nearby and you had already keyed it. That is not a style, it is a rut, and it is visible to everyone
1174
+ except you.
1175
+
1176
+ | What the raw has collected in `DELIVERED.tsv` | What you may do with it |
1177
+ |---|---|
1178
+ | nothing — never delivered | **one use.** It is unproven no matter how good the contact sheet looked |
1179
+ | an approval, no passes | **reuse it across other clients**, once each. This is what a proven raw is for |
1180
+ | passed on twice, never approved | **retire it.** Two buyers said no to that face. The third will too |
1181
+ | mixed | a fit question, not a quality one. Reuse it only where the pain genuinely matches |
1182
+
1183
+ **The shelf is public, which makes this sharper than it looks.** You cast from `vidfarm public-raws`
1184
+ and so does every other clipper on the marketplace. A slug that is easy to find is easy for everyone
1185
+ to find, so the buyer may have seen it this week from somebody else — and 851 MemeScreens means the
1186
+ excuse "there was nothing else" is almost never true. Cast past the first 200 (Appendix C) before
1187
+ you reach for a clip you have already used.
1188
+
1189
+ **The number, if you want one:** the same `clip_slug` in more than about **3 of your last 20
1190
+ deliveries** is a signature you did not choose. Either it has the approvals to justify it, or you
1191
+ are casting from memory instead of from the shelf.
1192
+
1193
+ ```bash
1194
+ cut -f5 CLIPPER/DELIVERED.tsv | tail -20 | sort | uniq -c | sort -rn | head
1195
+ ```
1196
+
1197
+ **No gate checks this and none can.** Gate 1 sees one composition, gate 2 sees one MP4, and neither
1198
+ has ever seen the other videos you sent that buyer. It is a **ledger** rule, not a frame rule: one
1199
+ row per delivery, checked before you cast. The marketplace-wide version, with the file format, is
1200
+ `https://vidfarm.cc/marketplace.md` § *never send one client the same video twice*.
1201
+
828
1202
  ---
829
1203
 
830
1204
  ## Cost-saving mode — the whole build at $0
@@ -890,6 +1264,46 @@ caption — you will learn more from five castings of one joke than from five jo
890
1264
  Keep the index (Appendix C) and a `captions.tsv` of rung-3 lines. The pairing is the creative act;
891
1265
  everything downstream is a loop.
892
1266
 
1267
+ ### ⚠️ The matrix is YOUR test set. It is not one client's inbox
1268
+
1269
+ Everything above is a testing discipline for an account you own, where five castings of one joke are
1270
+ five data points and the audience never sees them as a set. **The moment these become proofs on the
1271
+ marketplace, that arithmetic inverts.** A buyer does not see five data points. They open a swipe deck
1272
+ and see the same joke five times from the same name, and the fifth card is not a fifth chance — it is
1273
+ the reason they stop opening your cards.
1274
+
1275
+ > **Vary the clip if you are testing. Vary the JOKE if you are delivering.**
1276
+
1277
+ | | You are testing (your own account, one client's paid campaign, an asked-for A/B set) | You are delivering (proofs to a buyer) |
1278
+ |---|---|---|
1279
+ | One caption × five clips | ✅ the point of the exercise | ❌ one idea in five costumes |
1280
+ | Five pains × one clip | ✅ | ❌ the buyer sees your one clip five times |
1281
+ | What varies between proofs | one variable, deliberately | the **pain**, the **caption**, and the **clip**, all three |
1282
+ | Who sees the set | an algorithm | one person, in one sitting, next to each other |
1283
+
1284
+ **So build the matrix, ship one cell of it.** The matrix is still the right way to work — sit down
1285
+ with the testimonials, write ten pains, cast twenty clips, and pair them. Then send this buyer the
1286
+ one best pairing, and hold the rest for the next brief or the next buyer. The pairings you did not
1287
+ send are not waste; they are your inventory, and they are why the second delivery is fast.
1288
+
1289
+ **This format repeats more invisibly than any other in the set, for two reasons.** The clip is
1290
+ borrowed, so two videos built from the same `slugId` are *literally the same footage* — not similar,
1291
+ identical. And the caption sits in one of only eight frames, so two different pains written in the
1292
+ same frame land as the same joke even when every word differs. `When I …` twice in a row is one
1293
+ voice; `When I …` then `<Entity> watching me …` is two.
1294
+
1295
+ **Log the creative key on every delivery, per client.** Four fields, and no two proofs to one buyer
1296
+ may match on any of them (see Rule 11):
1297
+
1298
+ ```
1299
+ client · clip_slug · caption frame · pain
1300
+ ```
1301
+
1302
+ `clip_slug` is already in the enactment declaration and the frame and the pain are already in the
1303
+ caption, so logging this costs one line appended to `CLIPPER/DELIVERED.tsv` at submit time.
1304
+ **Read it back before you cast, not before you render** — the clip is the part you cannot edit, and
1305
+ finding the repeat after the composite is a rebuild.
1306
+
893
1307
  ---
894
1308
 
895
1309
  ## Quality gates — what is ENFORCED, and by what
@@ -899,9 +1313,15 @@ different things. **Running only the first one is the same as running none.**
899
1313
 
900
1314
  | Gate | Tool | Sees | Catches |
901
1315
  |---|---|---|---|
902
- | 1. Markup | `vidfarm qa <dir> --harness ./experimental/meme-recaption.md` | the composition DOM | duration, aspect, font regime, banned strings, layer counts. **22 checks, zero pixels** |
903
- | 2. Render | `python3 meme-qa.py <render>.mp4 <composition>.html` (Appendix E) | the finished MP4 | blank thumbnail, empty tail, residual green, caption centring/balance/placement, background not pushed down, silent audio, caption grammar |
904
- | 3. Human | your eyes | the joke | the three-way lock. **No script will ever check whether it is funny** |
1316
+ | 1. Markup | `vidfarm qa <dir> --harness ./experimental/meme-recaption.md` | the composition DOM | duration, aspect, font regime, banned strings, layer counts. **Zero pixels.** One check per `forbid_text` phrase, so the total moves whenever that list grows — do not quote a number here, it will be wrong within a month |
1317
+ | 2. Render | `python3 meme-qa.py <render>.mp4 <composition>.html` (Appendix E) | the finished MP4 **and the enactment declaration** | blank thumbnail, empty tail, residual green, caption centring/balance/placement, background not pushed down, silent audio, caption grammar, context debt, borrowed lore, and the declared enactment |
1318
+ | 3. Human | your eyes, and a pen | the joke | **the two covers**, written down and compared — then the three-way lock. **No script will ever check whether it is funny** |
1319
+
1320
+ **And one rule sits outside all three, on purpose: Rule 11.** Every gate here sees exactly one video.
1321
+ Whether you have already sent this client that clip, that caption frame or that pain is a fact about
1322
+ your *other* videos, so no check that opens this composition can ever reach it. It lives in
1323
+ `CLIPPER/DELIVERED.tsv` and it is checked **before you cast**, which is also the only moment it is
1324
+ cheap.
905
1325
 
906
1326
  > 🚨 **Gate 1 passes on a broken video.** Every defect this format actually produces is invisible to
907
1327
  > a DOM check. During the run that produced this file, a build passed `vidfarm qa` **19/19 while
@@ -936,7 +1356,7 @@ What gate 2 enforces, and the number it uses:
936
1356
  | `background_not_blown_out` | top-strip luma < 150 | A bright background eating a white caption |
937
1357
  | `audio_level` | −20…−13 LUFS, **or** < −50 with `--silent-ok` | A silent clip shipped as if it had sound |
938
1358
  | `caption_word_count` | 8–20 words | Slogan, or unreadable in 0.6s |
939
- | `caption_uses_a_frame` | one of the six | A caption that is a statement, not an experience |
1359
+ | `caption_uses_a_frame` | one of the **eight** frames | A caption that is a statement, not an experience |
940
1360
  | `caption_no_cta_or_resolution` | no CTA strings, no `so i` / `then i found` | Rules 1 and 2 — the ad leaking back in |
941
1361
  | `caption_lines_hand_broken` | a `<br>` is present | Renderer wrap landing mid-thought |
942
1362
  | `offer_named_somewhere` | in the caption **or** in the handle | An untraceable meme that builds someone else's page. Also prints which tier you are on |
@@ -947,14 +1367,31 @@ What gate 2 enforces, and the number it uses:
947
1367
  | `handle_is_dimmed` | alpha ≤0.85 *(only if a handle exists)* | Same |
948
1368
  | `single_shot` | exactly 1 `<video>` | "There are no beats" |
949
1369
  | `no_animation_on_caption` | no `animation` in `.memecap` | Kinetic captions, which break the read order |
1370
+ | `caption_no_context_debt` | none of `iykyk` / `if you know you know` / `you already know` / `part 2` / `as promised` / `like i said` / `see my last` … | Rule 10 — a caption asking the viewer for setup they were never given |
1371
+ | `enactment_declared` | the `<!-- meme-recaption: enactment -->` block, all seven fields, both blind reads ≥5 words and not the same sentence | Rule 3 — the two-cover test not run at all. An undeclared enactment is not a passing enactment |
1372
+ | `enactment_verbs_match` | `caption_verb` == `clip_verb`, both in the ten tokens | Rule 3.1 — vibe-adjacency, declared out loud. The clip does one thing and the caption is about another |
1373
+ | `intensity_mismatched` | `feeling_size` ≠ `clip_size`, both `small`/`large` | Rule 3.2 — a big feeling on a big reaction is a re-enactment; a small one on a small one is nothing |
1374
+ | `clip_read_is_independent` | `clip_reads_blind` shares no content word with the caption (stemmed, ≥4 chars, not a stopword) | Rule 3 — you described the clip with the caption's nouns, so either you never saw it cold or it is a re-enactment. **The gate cannot check that the two reads name one situation** — that stays gate 3 |
1375
+ | `caption_does_not_borrow_lore` | no content token of `clip_slug` (≥4 chars, stemmed, minus the offer's name) appears in the caption | Rule 10, context 3 — a joke that needs the source meme. Also catches the literal case, where the caption names what the clip already *is* |
950
1376
 
951
1377
  **Pass `--offer <domain>`** or the handle checks are skipped silently — an unset offer is not a
952
1378
  passing offer. **Pass `--pole win`** only when the caption is deliberately a win meme; the gate
953
1379
  defaults to `pain`, the stricter of the two, so nobody accidentally ships a resolution.
954
1380
 
955
- **Two things gate 2 deliberately does NOT check**, because a script cannot: whether the meme enacts
956
- the caption's verb, and the rights tier. Those stay on the human checklist below, and the rights
957
- tier stays in the build log in writing.
1381
+ > **Amended gate 2 now reaches into enactment, halfway.** This paragraph used to say gate 2
1382
+ > deliberately does not check *"whether the meme enacts the caption's verb, and the rights tier,
1383
+ > because a script cannot."* Half of that was true and half was an excuse. A script cannot judge a
1384
+ > match; it **can** hold you to a declaration and check the declaration against itself. So the
1385
+ > enactment block is now mandatory and five checks run on it, and the sentence is narrowed:
1386
+ >
1387
+ > **What gate 2 still cannot do** is tell you the declaration is *honest* — that the sentence you
1388
+ > wrote about the clip is what a stranger would actually write. That is gate 3, and it is why the
1389
+ > two covers stay on the human checklist below. **The rights tier is unchanged and remains
1390
+ > unchecked**, in the build log, in writing.
1391
+ >
1392
+ > Do not read the five green enactment lines as "the match is verified". Read them as "the
1393
+ > comparison was performed and its answers do not contradict each other". That is a much smaller
1394
+ > claim, and it is the one the gate is entitled to make.
958
1395
 
959
1396
  **Calibrate a checker before you trust it.** Two of these thresholds were wrong on their first
960
1397
  version and fired on a correct build — `caption_lines_balanced` measured pixel rows instead of text
@@ -969,15 +1406,38 @@ you have already looked at and agree is good, and make it pass before you let it
969
1406
 
970
1407
  Run this **before** you build. An unchecked box is a rewrite, not a fix in the edit.
971
1408
 
1409
+ **The client** — delivery work only. Skip the whole block on an account you own
1410
+ - [ ] `CLIPPER/DELIVERED.tsv` read for this client **before casting**, not before submitting
1411
+ - [ ] `clip_slug` not already delivered to them (Rule 11 — the same slug is the *same footage*)
1412
+ - [ ] Caption frame not already delivered to them — there are only eight
1413
+ - [ ] The pain not already delivered to them, restaged
1414
+ - [ ] If any of the three repeat: the client asked for a series **in writing**, and you can quote it
1415
+ - [ ] **Reusing this clip on a DIFFERENT client?** It has an approval on it, or it is unproven and
1416
+ this is its one outing. Passed on twice with no approval = retired
1417
+ - [ ] This `clip_slug` is not in more than ~3 of your last 20 deliveries
1418
+ - [ ] You read past the first 200 of the shelf (Appendix C) before reaching for a clip you own
1419
+ - [ ] The style — plate, font, band, grade, handle — is **identical** to the last delivery. That part
1420
+ is meant to repeat
1421
+
972
1422
  **The joke**
973
1423
  - [ ] **Pole chosen and written down** — pain or win (default pain)
974
1424
  - [ ] If win: the comedy is the size of the relief, not the feature
975
1425
  - [ ] `WHO:` and `MOMENT:` are written at the top of the build log
976
- - [ ] Test 1 — someone who has never heard of the offer laughs
1426
+ - [ ] Test 1 — someone with no knowledge of the offer, your last post, or the source meme laughs
977
1427
  - [ ] Test 2 — the experience named is one the product removes
978
- - [ ] Test 3 — the meme's physical verb IS the caption's feeling
979
- - [ ] The clip's INTENSITY mismatches the feeling's size (that mismatch is the joke)
1428
+ - [ ] Test 3 — **the two covers are written down**, not just felt:
1429
+ - [ ] Cover the caption `clip_reads_blind:` one sentence, what a stranger names in the clip
1430
+ - [ ] Cover the visual → `caption_reads_blind:` one sentence, the situation the caption leaves
1431
+ - [ ] The two sentences are **the same situation**, told twice. Not adjacent. Not compatible
1432
+ - [ ] `caption_verb` == `clip_verb`, both from the ten tokens
1433
+ - [ ] `feeling_size` ≠ `clip_size` — the clip's INTENSITY mismatches the feeling's (that is the joke)
1434
+ - [ ] The clip does the caption's **verb** but does not depict its **nouns** (no re-enactment)
1435
+ - [ ] `clip_reads_blind` reuses **none** of the caption's words (if it does, you projected)
1436
+ - [ ] All seven enactment fields are in `composition.html` as an HTML comment, not a layer
980
1437
  - [ ] Cold-viewer test — cover the brand and every feature word; does it still read?
1438
+ - [ ] **Rule 10** — nothing borrowed from the product, your last post, the meme's lore, or your life
1439
+ - [ ] Every specific survived **"who had to have been there?"** — detail kept, context cut
1440
+ - [ ] Context cut, and the freed words **re-spent on detail** (not just deleted)
981
1441
  - [ ] The caption is on rung 3: a number, a proper noun, a deflating outcome
982
1442
  - [ ] The numbers and proper nouns are traceable to the customer's own copy
983
1443
  - [ ] One of the eight frames, unmixed, subject first, 8–20 words, no end punctuation
@@ -1014,8 +1474,14 @@ Run this **before** you build. An unchecked box is a rewrite, not a fix in the e
1014
1474
  - [ ] Audio kept and normalised, measured on the render — or deliberately silent and said so
1015
1475
  - [ ] **Gate 1:** `vidfarm qa <dir> --harness ./experimental/meme-recaption.md`
1016
1476
  - [ ] **Gate 2:** `python3 meme-qa.py <render>.mp4 <dir>/composition.html` exits 0
1017
- - [ ] **Gate 3:** you watched it
1477
+ - [ ] **Gate 3 the whole-render pass, in this order:**
1478
+ - [ ] Watch it muted with the caption covered. Does the clip still *do something namable*?
1479
+ - [ ] Read the caption alone, out loud, cold. Is it already a complete joke?
1480
+ - [ ] Watch it whole, once, at speed. Did the recognition fire, or did you only *approve* of it?
1481
+ - [ ] Ask the last one honestly: **would a stranger need one question answered?** If yes, Rule 10
1018
1482
  - [ ] Post caption written, carrying the one comment ask
1483
+ - [ ] **Delivery work:** one row appended to `CLIPPER/DELIVERED.tsv` — `client · clip_slug · frame ·
1484
+ pain`. An unlogged delivery is the repeat you will ship next week
1019
1485
 
1020
1486
  ---
1021
1487
 
@@ -1026,11 +1492,18 @@ Diagnose by charge. Do not declare the video bad.
1026
1492
  | Symptom | The weak charge | The actual cause, in order of likelihood |
1027
1493
  |---|---|---|
1028
1494
  | Almost no views | Hook | The first three words are generic · the frame-0 image is not weird enough · the caption is too long to read in 0.6s |
1029
- | Views, everyone leaves at 3s | Loop / enactment | The action landed before the caption was read (Rule 4) · the meme does not enact the verb (Test 3) |
1495
+ | Views, everyone leaves at 3s | Loop / enactment | The action landed before the caption was read (Rule 4) · the meme does not enact the verb (Rule 3, test 3) |
1496
+ | **Watched to the end. Nothing. No leave, no laugh, no share** | **Enactment** | **Vibe-adjacency.** The clip was compatible with the caption instead of being the same event. This is the default cause and it is the one you cannot see, because you read the caption first. Run the two covers on the shipped video — write both sentences down before you look at them together |
1030
1497
  | Watched to the end, no reaction | Payoff / specificity | Rung 2. Add the number and the proper noun · or the joke resolves (Rule 2) |
1498
+ | Only your existing followers engaged; no cold reach | **Rule 10** | Context debt. The caption borrowed from your last post, or the product, or the meme's lore. The algorithm did try strangers; the strangers needed a briefing |
1499
+ | It reached people who love the meme, not people with the problem | Rule 10, context 3 | The joke was about the meme, not about their week. Ceiling = the meme's fandom (`caption_does_not_borrow_lore`) |
1500
+ | "True but not funny" · reads like a caption on a stock photo | Rule 3.2 / 3.3 | The sizes matched (a big feeling on a big reaction = re-enactment) · or the clip depicted the caption's nouns instead of its verb |
1501
+ | Nobody in the niche felt seen; it could be about anyone | **Strictness overshot** | You cut context and did not re-spend the words on detail. Rung 1. Go back to Rule 6 and take a sentence a real user said |
1031
1502
  | Good watch time, dead comments | Bait | No post caption ask · or the ask is a poll, not an invitation to add their own version |
1032
1503
  | Comments are "what is this an ad for" | Rule 1 | The product leaked in — a logo, a screenshot, a category noun in the caption |
1033
1504
  | Reach collapsed after 2 hours | Audio / rights | A music-sync meme got muted · a 🔴 clip got claimed |
1505
+ | **The client passed on a video that would have worked on its own** | **Rule 11** | Repetition. They already had this clip, this caption frame, or this pain from you. It is judged against your *last* delivery, not on its own — check `DELIVERED.tsv` for the collision, then change the pain and cast fresh |
1506
+ | The client's approvals dropped off across a batch, from the first card to the last | **Rule 11** | You shipped a row of the matrix instead of one video. Five castings of one joke is a test set, not a delivery |
1034
1507
 
1035
1508
  ---
1036
1509
 
@@ -1246,6 +1719,19 @@ A static, plated card: block centred, words left-aligned, no animation, `data-st
1246
1719
  This is the exact markup from the worked example, which measures 49.8% block centre.
1247
1720
 
1248
1721
  ```html
1722
+ <!-- meme-recaption: enactment
1723
+ Rule 3 + Rule 10, on the record. A COMMENT, never a layer: a layer would count
1724
+ against max_text_cards, would need a data-start, and could render. Gate 2 reads
1725
+ it out of this file, so there is no second file to lose.
1726
+ clip_slug: my-disappointment-is-immeasurable-and-my-day-is-ruined
1727
+ caption_verb: deflate
1728
+ clip_verb: deflate
1729
+ feeling_size: small
1730
+ clip_size: large
1731
+ clip_reads_blind: a man in a suit goes flat and accepts something without arguing
1732
+ caption_reads_blind: someone gives up on dinner after an hour and settles for pizza
1733
+ -->
1734
+
1249
1735
  <!-- Layout B: caption on a translucent plate over the background, above the subject.
1250
1736
  The WRAPPER is full width and centres; the PLATE is an inline-block that rides
1251
1737
  that centre. left:7% would pin the plate to 7% and leave the frame lopsided. -->
@@ -1406,7 +1892,7 @@ MOMENT: the 40 minutes between the craving and giving up on it
1406
1892
  | | |
1407
1893
  |---|---|
1408
1894
  | Frame | `When I …` — the complaint is embarrassing, so first person |
1409
- | Verb cast for | *deflating; accepting a bad outcome* |
1895
+ | Verb cast for | `deflate` shoulders drop, the bad outcome is accepted without a fight |
1410
1896
  | Caption | `when i've had 10 tabs open` / `since 7pm for one scallop roll` / `and we're getting pizza again` |
1411
1897
  | Clip (🟡) | `my-disappointment-is-immeasurable-and-my-day-is-ruined` · 1280×720 · plate `0x00FF00` digital · key 0.34 |
1412
1898
  | Clip (🟢) | `hangover-dog` · 2160×3840 · plate `0x00DA00` filmed · key **0.22** |
@@ -1425,9 +1911,39 @@ and the object (scallop roll) are the customer's own words, per Rule 6.
1425
1911
  > the company. **When a real brand name is what makes your caption feel sharp, that is the signal to
1426
1912
  > replace it, not to keep it.**
1427
1913
 
1428
- **Three-way lock:** *standalone* 10 tabs since 7pm ending in pizza is funny with no context;
1429
- *problem space* — deleting that hour is the product's entire pitch; *enactment* — both clips hold a
1430
- deadpan while something disappointing is already true.
1914
+ **The enactment declaration, as shipped** (back-filled onto both builds when the block became
1915
+ mandatory):
1916
+
1917
+ ```
1918
+ clip_slug: my-disappointment-is-immeasurable-and-my-day-is-ruined
1919
+ caption_verb: deflate
1920
+ clip_verb: deflate
1921
+ feeling_size: small # one annoying evening
1922
+ clip_size: large # a day that is immeasurably ruined
1923
+ clip_reads_blind: a man in a suit goes flat and accepts something without arguing
1924
+ caption_reads_blind: someone gives up on dinner after an hour and settles for pizza
1925
+ ```
1926
+
1927
+ **Read the two blind lines against each other.** They share no vocabulary at all — no "tabs", no
1928
+ "flat", nothing — and they are plainly one situation. That is what a correct pair looks like, and
1929
+ it is why `clip_read_is_independent` bans overlap instead of requiring it: the clip's world (a man
1930
+ in a suit) and the caption's world (dinner, tabs, pizza) are *supposed* to be different. The verb
1931
+ is the only thing they have in common, and the verb is the one thing that is declared rather than
1932
+ described.
1933
+
1934
+ **Sizes:** `small` vs `large`. One annoying evening arriving in the body of a man whose day is
1935
+ immeasurably ruined. Same verb, wrong size — Rule 3.1 and 3.2 at once, and that gap is the laugh.
1936
+
1937
+ **Rule 10, checked field by field:** nothing here needs the product (`10 tabs`, `7pm`, `scallop
1938
+ roll` are the niche's week), nothing needs a previous post, nothing needs the source meme — the
1939
+ caption never says "disappointment", "immeasurable" or "ruined", so `caption_does_not_borrow_lore`
1940
+ passes — and nothing needs to have been *you*: **who had to have been there?** Anyone who has ever
1941
+ tried to pick dinner.
1942
+
1943
+ **Three-way lock:** *standalone* — 10 tabs since 7pm ending in pizza is funny with no context, no
1944
+ previous post and no knowledge of the meme; *problem space* — deleting that hour is the product's
1945
+ entire pitch; *enactment* — both blind reads name one situation, both clips hold a deadpan while
1946
+ something disappointing is already true.
1431
1947
 
1432
1948
  **The background is the joke's world, not the product's.** An empty restaurant at night is the place
1433
1949
  they never got to. Blurred so it reads as a set, dark so the plate and the keyed subject both pop.
@@ -1435,6 +1951,12 @@ they never got to. Blurred so it reads as a set, dark so the plate and the keyed
1435
1951
  **Gates:** `vidfarm qa` 18/18 · `meme-qa.py` **26/26** on both builds (block centre measured at
1436
1952
  **49.8%**). The 🟢 build needs `--silent-ok`, and fails without it — which is the check working.
1437
1953
 
1954
+ > Those two counts are **historical**, from the run that produced this file. Both gates have grown
1955
+ > since: gate 1 gained five `forbid_text` phrases and gate 2 gained the six Rule 3 / Rule 10
1956
+ > checks. The builds still pass — the declaration above is what was back-filled to make that true —
1957
+ > but do not treat `18/18` or `26/26` as the number you should see. **A check count is not a
1958
+ > measurement of anything; the verdict is.**
1959
+
1438
1960
  **Naming the offer — both tiers, built and measured.**
1439
1961
 
1440
1962
  | | **v003 — tier 1** ✅ preferred | **v001 / v002 — tier 2** |
@@ -1486,6 +2008,12 @@ names the product. All of those pass `vidfarm qa` today.
1486
2008
 
1487
2009
  This checks the finished MP4 and the composition text together, and EXITS 1.
1488
2010
 
2011
+ It also reads the `<!-- meme-recaption: enactment -->` declaration out of the
2012
+ composition (Rule 3, Rule 10). It cannot judge whether the clip enacts the
2013
+ caption — nothing can — but it CAN require that the two-cover test was performed
2014
+ and that its answers do not contradict each other. That is a smaller claim than
2015
+ "the match is verified", and it is the one this script is entitled to make.
2016
+
1489
2017
  usage: meme-qa.py <render.mp4> <composition.html> [--offer dishcover.io]
1490
2018
  [--pole pain|win] [--silent-ok] [--json]
1491
2019
  """
@@ -1509,6 +2037,55 @@ def check(name, ok, detail, fatal=True):
1509
2037
  results.append({"check": name, "ok": bool(ok), "detail": detail, "fatal": fatal})
1510
2038
 
1511
2039
 
2040
+ # --------------------------------------------- word tools for Rule 3 / Rule 10
2041
+ # These decide FATAL checks, so they are deliberately crude in the direction of
2042
+ # UNDER-firing. A gate that cries wolf on a correct build gets switched off
2043
+ # within a day, which is worse than not having it — see "Calibrate a checker
2044
+ # before you trust it". Concretely: a 4-letter floor (so `cat`, `dog`, `huh` in
2045
+ # a slug are ignored), a stemmer that is a suffix chop and misses give/giving,
2046
+ # and a stopword list that gives up any word that is also ordinary English.
2047
+ STOP = {
2048
+ "that", "this", "with", "when", "what", "your", "have", "from", "they", "them",
2049
+ "their", "there", "then", "than", "into", "just", "been", "being", "will",
2050
+ "would", "could", "should", "about", "over", "under", "some", "more", "most",
2051
+ "very", "really", "going", "gonna", "still", "again", "like", "know", "make",
2052
+ "made", "take", "took", "look", "time", "times", "thing", "things", "something",
2053
+ "someone", "anyone", "everyone", "nothing", "anything", "everything", "without",
2054
+ "while", "after", "before", "because", "which", "where", "here", "does", "much",
2055
+ "many", "other", "another", "same", "each", "every", "both", "since", "getting",
2056
+ "gets", "went", "goes", "want", "wants", "need", "needs", "keep", "keeps",
2057
+ "meme", "clip", "screen", "green", "video", "person", "people", "guy", "girl",
2058
+ }
2059
+
2060
+
2061
+ def stem_word(w):
2062
+ """A suffix chop, not a linguist. Enough to tie accepts/accepting together.
2063
+
2064
+ NOT called `stem`: section 7b binds a local `stem = OFFER.split(".")[0]` for
2065
+ the offer's name, and a helper called `stem` gets shadowed by it halfway down
2066
+ the file. The first run of this section died exactly there.
2067
+ """
2068
+ for suf in ("ies", "ing", "ed", "es", "s"):
2069
+ if w.endswith(suf) and len(w) - len(suf) >= 4:
2070
+ return w[: -len(suf)] + ("y" if suf == "ies" else "")
2071
+ return w
2072
+
2073
+
2074
+ def content(text):
2075
+ """Stemmed content words of >=4 LETTERS. Apostrophes and digits are dropped
2076
+ on purpose: `i've`, `we're`, `10` and `7pm` carry no comparison value, and
2077
+ the numbers are the caption's best specifics precisely because they are
2078
+ shared, not private."""
2079
+ out = set()
2080
+ for w in re.findall(r"[a-z]{4,}", text.lower()):
2081
+ if w in STOP:
2082
+ continue
2083
+ s = stem_word(w)
2084
+ if s not in STOP:
2085
+ out.add(s)
2086
+ return out
2087
+
2088
+
1512
2089
  def probe(path, vf, w, h, pre=(), post=("-vframes", "1")):
1513
2090
  # -ss/-sseof are INPUT options and go before -i; -vframes is an OUTPUT option
1514
2091
  # and goes after. Getting that backwards decodes nothing and reports no frame.
@@ -1647,7 +2224,7 @@ else:
1647
2224
  r"^my \w+ when\b", r"^why my\b",
1648
2225
  r"^[\w][\w'.\- ]{0,24} (when|with|after|watching|reading|seeing|hearing) ")
1649
2226
  check("caption_uses_a_frame", any(re.search(f, text_cap, re.I) for f in FRAMES),
1650
- f"must open with one of the seven frames — got {' '.join(words[:4])!r}")
2227
+ f"must open with one of the eight frames — got {' '.join(words[:4])!r}")
1651
2228
  check("caption_is_lowercase_start", text_cap[:1].islower(),
1652
2229
  "meme captions are typed, not headlined", fatal=False)
1653
2230
  # the product must not be in the video (Rule 1), and it must not resolve (Rule 2)
@@ -1659,6 +2236,19 @@ else:
1659
2236
  # hand-broken lines are required — the renderer's own wrap lands mid-thought
1660
2237
  check("caption_lines_hand_broken", "<br" in cap.group(1),
1661
2238
  "use <br> at the clause; never let the renderer wrap a meme caption")
2239
+ # Rule 10 — CONTEXT DEBT. Each of these asks the viewer for something they were
2240
+ # never given: a previous post, the source meme's lore, or a shared secret. The
2241
+ # platform serves this video cold to strangers, on purpose, so the debt is never
2242
+ # repaid — it just quietly costs you everyone who is not already following you.
2243
+ # "again" is deliberately NOT here: `and we're getting pizza again` is the
2244
+ # collapse of the worked example, and it borrows nothing.
2245
+ DEBT = ("iykyk", "if you know you know", "you know the one", "you already know",
2246
+ "part 2", "part two", "part 3", "part three", "as promised",
2247
+ "like i said", "as i said", "as mentioned", "see my last",
2248
+ "from my last", "same as last time", "remember when i said")
2249
+ dhit = [d for d in DEBT if d in text_cap.lower()]
2250
+ check("caption_no_context_debt", not dhit,
2251
+ f"context debt {dhit} — the viewer has no previous slide and no briefing (Rule 10)")
1662
2252
 
1663
2253
  # ------------------------------------------------- 7b. naming the offer
1664
2254
  # The offer IS named on the video. Two ways, in priority order:
@@ -1734,6 +2324,80 @@ if OFFER:
1734
2324
  check("handle_is_dimmed", bool(dim) and float(dim.group(1)) <= 0.85,
1735
2325
  f"handle alpha {dim.group(1) if dim else 'opaque'} (<=0.85)", fatal=False)
1736
2326
 
2327
+ # ------------------------------------- 7c. the enactment declaration (Rule 3)
2328
+ # The two-cover test is a human act, and no script will ever judge whether a clip
2329
+ # enacts a caption. What a script CAN do is refuse a build where the test was
2330
+ # never performed, or where its own answers contradict each other. So the answers
2331
+ # live in the composition as an HTML COMMENT — a comment and not a layer, because
2332
+ # a layer counts against max_text_cards and could render, and this must never
2333
+ # reach a pixel.
2334
+ VERBS = {"deflate", "blank", "loop", "smug", "overreact",
2335
+ "correct", "unbothered", "celebrate", "stare", "wait"}
2336
+ FIELDS = ("clip_slug", "caption_verb", "clip_verb", "feeling_size", "clip_size",
2337
+ "clip_reads_blind", "caption_reads_blind")
2338
+
2339
+ decl_m = re.search(r"<!--\s*meme-recaption:\s*enactment\b(.*?)-->", html, re.S)
2340
+ decl = {}
2341
+ if decl_m:
2342
+ for line in decl_m.group(1).splitlines():
2343
+ kv = re.match(r"\s*([a-z_]+)\s*:\s*(.+?)\s*$", line)
2344
+ if kv and kv.group(1) in FIELDS:
2345
+ decl[kv.group(1)] = re.sub(r"\s*#.*$", "", kv.group(2)).strip()
2346
+
2347
+ missing = [f for f in FIELDS if not decl.get(f)]
2348
+ short = [f for f in ("clip_reads_blind", "caption_reads_blind")
2349
+ if len(decl.get(f, "").split()) < 5]
2350
+ same = (decl.get("clip_reads_blind", "x").lower() ==
2351
+ decl.get("caption_reads_blind", "y").lower())
2352
+ check("enactment_declared",
2353
+ bool(decl_m) and not missing and not short and not same,
2354
+ ("no <!-- meme-recaption: enactment --> block — run the two covers and write them down"
2355
+ if not decl_m else
2356
+ f"missing {missing}" if missing else
2357
+ f"{short} must be a real sentence (>=5 words)" if short else
2358
+ "the two blind reads are the same sentence — you wrote one and pasted it twice"
2359
+ if same else "7 fields, both reads written"))
2360
+
2361
+ if decl_m and not missing:
2362
+ cv, kv_ = decl["caption_verb"].lower(), decl["clip_verb"].lower()
2363
+ # Rule 3.1. Two different tokens is a mismatch you declared out loud; a token
2364
+ # outside the ten is a verb nobody can compare, which is the same as no verb.
2365
+ check("enactment_verbs_match",
2366
+ cv in VERBS and kv_ in VERBS and cv == kv_,
2367
+ f"caption_verb={cv!r} clip_verb={kv_!r} — must be the SAME token from {sorted(VERBS)}")
2368
+ # Rule 3.2. Two coarse buckets on purpose: a 1-5 scale invites a 3-vs-4 that
2369
+ # means nothing, and the only question the format asks is binary — is the
2370
+ # reaction the wrong size for the cause?
2371
+ fs, cs = decl["feeling_size"].lower(), decl["clip_size"].lower()
2372
+ check("intensity_mismatched",
2373
+ {fs, cs} == {"small", "large"},
2374
+ f"feeling_size={fs!r} clip_size={cs!r} — must be small/large and DIFFERENT "
2375
+ f"(matched sizes are a re-enactment, not a joke)")
2376
+ # Rule 3.3 + the read-order problem. If the only way you can describe the clip
2377
+ # is with the caption's own nouns, either you never saw it cold (you projected
2378
+ # the caption onto it) or the clip really does depict the caption's nouns,
2379
+ # which is a re-enactment. A correct pair shares NO vocabulary — the clip's
2380
+ # world and the caption's world are supposed to be different. This is why the
2381
+ # check bans overlap instead of requiring it: an earlier draft required the two
2382
+ # reads to share a word, and every correct build failed.
2383
+ if cap:
2384
+ bleed = sorted(content(decl["clip_reads_blind"]) & content(text_cap))
2385
+ check("clip_read_is_independent", not bleed,
2386
+ f"clip_reads_blind reuses the caption's words {bleed} — describe the clip, "
2387
+ f"not your plan for it")
2388
+ # Rule 10, context 3 — the source meme's own lore. A word that comes from the
2389
+ # meme's NAME comes from the meme's fandom, not from the audience's week, and
2390
+ # it caps reach at people who recognise the clip. It also catches the literal
2391
+ # case, where the caption names what the clip already is.
2392
+ if cap:
2393
+ offer_stem = OFFER.split(".")[0].lower() if OFFER else ""
2394
+ slug_words = content(decl["clip_slug"].replace("-", " ").replace("_", " "))
2395
+ slug_words.discard(stem_word(offer_stem))
2396
+ lore = sorted(slug_words & content(text_cap))
2397
+ check("caption_does_not_borrow_lore", not lore,
2398
+ f"caption reuses slug words {lore} — the clip is borrowed equipment, "
2399
+ f"not a shared reference (Rule 10)")
2400
+
1737
2401
  # --------------------------------------------- 8. one shot, no brand chrome
1738
2402
  check("single_shot", len(re.findall(r"<video\b", html)) == 1,
1739
2403
  f"{len(re.findall(r'<video', html))} video layer(s) — a meme recaption is ONE shot")