@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.
- package/.agents/skills/vidfarm/SKILL.md +2 -2
- package/.agents/skills/vidfarm/recipes/retheme-template.md +6 -5
- package/.agents/skills/vidfarm/references/core-workflows.md +4 -0
- package/.agents/skills/vidfarm/references/editor-workflows.md +25 -0
- package/SKILL.director.md +37 -7
- package/SKILL.md +7 -2
- package/dist/src/cli.js +7 -1
- package/dist/src/devcli/clipper-run.js +8 -0
- package/dist/src/devcli/marketplace-gigs.js +307 -4
- package/dist/src/devcli/proof-verify.js +465 -0
- package/experimental/flash-harness.md +142 -18
- package/experimental/meme-recaption.md +701 -37
- package/experimental/sticker-slideshow-tips.md +511 -86
- package/marketplace.md +406 -11
- package/package.json +4 -1
- package/update.md +21 -1
|
@@ -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
|
|
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** —
|
|
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
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
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
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
|
335
|
-
|
|
336
|
-
|
|
|
337
|
-
|
|
|
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 —
|
|
1021
|
+
### Rule 3 — the clip ENACTS the caption. Same verb, wrong size, different world
|
|
767
1022
|
|
|
768
|
-
Written above as the
|
|
769
|
-
casting and the timeline
|
|
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. **
|
|
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
|
|
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
|
-
**
|
|
956
|
-
|
|
957
|
-
|
|
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
|
|
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
|
|
979
|
-
- [ ]
|
|
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
|
|
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 (
|
|
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 |
|
|
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
|
-
**
|
|
1429
|
-
|
|
1430
|
-
|
|
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
|
|
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")
|