Add Beryl Agent and harness source
This commit is contained in:
@@ -0,0 +1,122 @@
|
||||
# Failure modes measured on this repository
|
||||
|
||||
The rules live in `SKILL.md`. This file is what was actually observed, with the
|
||||
numbers. Read it when a rule looks arbitrary, or when you are tempted to fix a
|
||||
bad render by doing more of what caused it.
|
||||
|
||||
## Negation summons the object — 2026-08-17
|
||||
|
||||
A pitcher-and-cups shot kept growing extra pouring vessels, so the retention
|
||||
line was hardened with an explicit ban:
|
||||
|
||||
```
|
||||
no second pitcher, no fifth cup, no jug, no bottle, no carafe
|
||||
```
|
||||
|
||||
The next render was **worse**: the pitcher now poured *into another pitcher*,
|
||||
the receiving cup having grown a spout. Deleting the whole list and rewriting
|
||||
the same constraint positively passed on the first try.
|
||||
|
||||
The text conditioning does not parse negation — `no jug` and `jug` enter
|
||||
cross-attention as the same token. Negating an *attribute* (`no lip`) is safe;
|
||||
what backfires is negating a whole object noun.
|
||||
|
||||
What worked instead: the pitcher became "the only vessel in this video that has
|
||||
a pouring lip"; the cups became `no lip and no handle` and `only ever receives
|
||||
liquid`; the count was stated positively as `the whole table holds five glass
|
||||
objects in total`.
|
||||
|
||||
## `<Picture N>` entries and `sources` — 2026-08-10
|
||||
|
||||
Both `qwen3.8-max` and `claude-opus-5` hit the same rejection three times each
|
||||
and burned five attempts between them.
|
||||
|
||||
The cause was the instruction, not the models. A picture's source is itself, so
|
||||
`sources: <Picture 1>` is a tautology — and the one thing a picture definition
|
||||
must do is say who is in that frame, which forces it to mention that the same
|
||||
person also appears in `<Picture 2>`. The set becomes `[1,2]` and the entry is
|
||||
rejected for a contradiction the format invented.
|
||||
|
||||
**The same error from two different model tiers means the instruction is
|
||||
ambiguous, not that the model is too weak.** Reaching for a bigger model there
|
||||
is the wrong move. Read the failing constraint and ask whether it carries any
|
||||
information at all.
|
||||
|
||||
## What freehand prompts produced
|
||||
|
||||
Observed on this repository's own production runs, each costing a re-render the
|
||||
Ref2VA structure would have caught for free:
|
||||
|
||||
- strands of vegetable appearing with no vegetable in frame
|
||||
- two copies of a single-item product in one shot
|
||||
- two tools merging onto one subject instead of staying on opposite sides of
|
||||
the board
|
||||
|
||||
## Audio collapse is a dice roll — 2026-08-09
|
||||
|
||||
One beat, an alarm bell held for the full 5 s, same prompt both times:
|
||||
|
||||
| | collapsed | re-rolled |
|
||||
|---|---|---|
|
||||
| last 1.4 s | −8.8 dB — loudest point in the clip | −16.5 dB |
|
||||
| span across the clip | 25.8 dB | 6.5 dB |
|
||||
| clipped samples | 844 | 85 |
|
||||
|
||||
Two explanations looked equally sound at the time — the sound was described too
|
||||
vaguely, and H3 cannot hold a sustained high-energy ambience. Both would have
|
||||
sent someone rewriting the prompt scaffold that every other clip depends on.
|
||||
One re-render falsified both: same wording, same sustained requirement, clean
|
||||
take.
|
||||
|
||||
One collapsed sample is never grounds for changing the scaffold. The trigger for
|
||||
that is the same prompt collapsing twice in a row — that is the difference
|
||||
between a bad die and a bad prompt.
|
||||
|
||||
## Object count is set by canvas size — 2026-08-20
|
||||
|
||||
A pitcher-and-four-cups delivery, the same product family as the negation case
|
||||
above. The brief's hard lock was "exactly one pitcher and four cups, countable".
|
||||
|
||||
The riskiest shot was probed three times at `--megapixels 0.3`. All three came
|
||||
back with exactly four cups. The same prompts at `1.03` grew a fifth in three of
|
||||
the six clips. Nothing about the prompt changed — only the canvas. At the low
|
||||
tier the frame is small and the model fills it with what was asked for; at full
|
||||
tier the extra horizontal room gets filled too.
|
||||
|
||||
So a cheap probe is evidence about motion, structure and composition, and no
|
||||
evidence at all about how many things appear.
|
||||
|
||||
What finally held was a container, not a phrase. The four cups were placed on a
|
||||
serving board defined as *four cups long and one cup deep, covered end to end*.
|
||||
Where the whole board stayed inside the frame the count was correct on the first
|
||||
render. Where a camera move cropped an end, the count drifted into the cropped
|
||||
space:
|
||||
|
||||
| shot | camera | attempts to correct |
|
||||
|---|---|---|
|
||||
| pitcher lift | fixed wide, whole board in frame | 1 |
|
||||
| pour | fixed wide, whole board in frame | 1 |
|
||||
| final settle | opens wide, only widens | 1 |
|
||||
| ice drops | close-up, then truck, then "static wide" wording | 3, fixed only by reusing a working shot's establishing geometry verbatim |
|
||||
|
||||
The last row is the useful one. Three separate rewordings of "keep all four in
|
||||
frame" did not move it. Copying the opening sentence from a shot that already
|
||||
rendered four correctly did. When an instruction has failed twice, stop
|
||||
rewording it and transplant the wording that works.
|
||||
|
||||
## The batch fingerprint hashed filenames — 2026-08-20
|
||||
|
||||
Same delivery. All six prompts were rewritten to fix the product-lock violation
|
||||
above, and `batch` returned `skip: unchanged` for all six: `job_fingerprint`
|
||||
hashed the prompt file's *path*, while its own docstring claimed it covered the
|
||||
same inputs as the single-render fingerprint, which hashes the prompt *text*.
|
||||
|
||||
The stale clips were caught by frame review, not by the tool. Fixed in
|
||||
`src/piecesai/h3/batch.py` — the fingerprint now digests prompt contents and
|
||||
reference bytes — with regression tests for a rewritten prompt, an edited
|
||||
anchor, and a moved-but-unchanged prompt file.
|
||||
|
||||
The general shape is the one already on the seed comparison: **a fingerprint
|
||||
match is a claim about work, and a claim you did not check is a result you did
|
||||
not get.** The seed version costs a wasted A/B. This version silently returns
|
||||
content you already know is wrong.
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
# How the render settings were chosen
|
||||
|
||||
The rules live in `SKILL.md`. This file is the evidence behind them — read it
|
||||
when you are about to argue with a default, not before every render.
|
||||
|
||||
The authority for the numbers is the comment block above
|
||||
`U06_V4_SAMPLING_STEPS` in `src/piecesai/generators/minimax_h3.py`. When the
|
||||
code default moves, that comment moves with it and this file is stale until it
|
||||
is rewritten from there. It has been stale once already: three commits of
|
||||
default changes landed after the first version was written, and it went on
|
||||
telling readers that the shipped default had been "rejected on motion".
|
||||
|
||||
## The v4 step600 LoRA at 8 steps
|
||||
|
||||
Chosen 2026-08-16 by side-by-side viewing across four rounds.
|
||||
|
||||
Everything before that round used the **fl2v** LoRA — trained for first/last
|
||||
frame work, while this deployment renders ref2va. Once that mismatch was fixed
|
||||
the comparison stopped being a speed-versus-quality trade, and two candidates
|
||||
were left: the ref2v-matched 4-step weight, and v4 step600 at 8 steps.
|
||||
|
||||
| round | owner picked |
|
||||
|---|---|
|
||||
| peeler, seed 661120 | ref2v + v4 |
|
||||
| peeler, seed 314159 | no-LoRA + v4 |
|
||||
| two-person exchange | ref2v + v4 |
|
||||
| Malay piece to camera | ref2v + v4 |
|
||||
|
||||
v4 is the only one present in every round. The ref2v-matched weight scored
|
||||
higher more often but dropped out when the seed changed, and a twelve-clip
|
||||
delivery cannot depend on drawing a good seed. The steadier of two equals wins.
|
||||
|
||||
8 steps is v4's own contract: its documentation reports motion smear at 4 steps
|
||||
under large fast motion, largely gone by 6-8, and no gain past 8. Measured on a
|
||||
5090 at 1.03 MP:
|
||||
|
||||
| setting | seconds per clip |
|
||||
|---|---|
|
||||
| v4 step600 @ 8 steps (current default) | 119-122 |
|
||||
| old fl2v default | 128 |
|
||||
| undistilled @ 14 steps | 156 |
|
||||
|
||||
Strength is tuned for 1.0 by the LoRA's own documentation. The 0.75 that shipped
|
||||
before sat below the documented range and under-corrected the velocity
|
||||
prediction, which is exactly what smears on large motion.
|
||||
|
||||
## Why a sharpness metric was rejected
|
||||
|
||||
A sharpness proxy was measured here once and ranked the runs the wrong way. It
|
||||
scored horizontal high-frequency energy, and over-sharp grain is precisely the
|
||||
failure mode these LoRAs name in their own documentation — so the metric
|
||||
rewarded the artefact.
|
||||
|
||||
Motion coherence needs eyes. Render the variants, show them, let the owner pick.
|
||||
|
||||
## Why an A/B needs a fresh seed
|
||||
|
||||
A render whose request fingerprint matches a completed run resumes from that
|
||||
run's receipt and returns the old file in seconds. Correct for resuming a batch;
|
||||
wrong for a comparison.
|
||||
|
||||
This cost two rounds on 2026-08-16. One set of four variants came back
|
||||
pixel-identical. Another "finished" in 3-5 seconds. Both times the giveaway was
|
||||
the clock, not the picture — the frames looked plausible either way.
|
||||
+101
@@ -0,0 +1,101 @@
|
||||
# Spoken language support
|
||||
|
||||
H3 renders its own audio. The words in `<d>…</d>` are *spoken by the model*,
|
||||
not dubbed on afterwards, so the language of a line is a model capability
|
||||
question — not a localisation question that post-production can fix.
|
||||
|
||||
## The eleven stable languages
|
||||
|
||||
MiniMax's own model card states:
|
||||
|
||||
> Stable support for 11 languages: Arabic, Chinese, English, French, German,
|
||||
> Italian, Japanese, Korean, Portuguese, Russian, and Spanish.
|
||||
> Additional languages are also supported to varying degrees.
|
||||
|
||||
Source: <https://huggingface.co/MiniMaxAI/MiniMax-H3>, checked 2026-08-20.
|
||||
|
||||
Use exactly these names as the tag inside `<d>`:
|
||||
|
||||
```
|
||||
<d>[English] First batch of the morning.</d>
|
||||
<d>[Chinese] 今天的第一炉。</d>
|
||||
```
|
||||
|
||||
## What the limit covers, and what it does not
|
||||
|
||||
- **Covered:** every spoken or sung line the model performs — dialogue,
|
||||
voiceover narration, lyrics. Anything inside `<d>…</d>`.
|
||||
- **Not covered:** the prompt body itself. All six rewrite sections stay in
|
||||
English regardless of the campaign language — that is `h3-prompt-writing`'s
|
||||
rule and this gate does not change it.
|
||||
- **Not covered:** on-screen text. Signs, lower thirds and product copy are a
|
||||
*glyph rendering* problem, not a speech one. A language can be outside the
|
||||
eleven and still render as visible text, and a language inside the eleven can
|
||||
still render its glyphs badly. Judge on-screen copy from a still.
|
||||
- **Not covered:** `overall_soundscape` and `non_diegetic_music`. Wordless
|
||||
audio has no language.
|
||||
|
||||
"Supported to varying degrees" is the vendor's phrasing for the rest. It is
|
||||
not a promise and it is not a refusal. Treat an out-of-list language as an
|
||||
untested render, priced like one.
|
||||
|
||||
The failure modes to watch for on an out-of-list line are accent drift toward
|
||||
the nearest stable language, substituted phonemes, lip movement that no longer
|
||||
matches the words, and — occasionally — the line coming back in English. None
|
||||
of these are measured on this repo's fleet; they are what the probe below is
|
||||
for.
|
||||
|
||||
## Subtitles are outside this gate, and outside the toolchain
|
||||
|
||||
Delivery subtitles carry no model risk in any language — they are not spoken.
|
||||
But no subtitle burn-in tool ships here: `post` has `verify`, `assemble`,
|
||||
`frames` and `derive` and nothing else. Burned-in captions are hand-written
|
||||
ffmpeg work. Price them as work, never as an existing step.
|
||||
|
||||
## The gate
|
||||
|
||||
Fires the moment a spoken language is chosen — at the brief, not at render
|
||||
time. The official creative Skills ask for narration language early
|
||||
(`brand-promo-video-generator` asks in Step 1, alongside duration and aspect
|
||||
ratio); that answer is the trigger.
|
||||
|
||||
`uv run piecesai h3 preflight --language <name> ...` answers this without
|
||||
reading anything: the language check is one of its findings, and it warns
|
||||
rather than fails, which is exactly the gate described here.
|
||||
|
||||
If the language is one of the eleven, say nothing and carry on.
|
||||
|
||||
If it is not, this is a **soft** gate. Do not refuse, do not silently swap the
|
||||
language, and do not quietly drop the voiceover. Tell the user plainly what
|
||||
the constraint is and let them pick:
|
||||
|
||||
1. **Keep the language, probe first.** Render the single densest dialogue
|
||||
shot at low `--megapixels` and listen to it before committing the batch.
|
||||
Cheapest way to turn the question into an answer.
|
||||
2. **Speak a stable language, subtitle the target one.** Voice the line in
|
||||
the nearest stable language and carry the campaign language as subtitles.
|
||||
Usually the right answer for a promo, where the read is short and the copy
|
||||
carries the message. Quote the subtitles as work — see above.
|
||||
3. **Drop the spoken layer.** Music, soundscape and on-screen copy only.
|
||||
Costs nothing in render risk and often suits a 15-second promo better than
|
||||
a rushed voiceover.
|
||||
|
||||
Record which one the user chose next to the prompt, the same way the prompt
|
||||
itself is kept. When a later shot comes back with wrong-sounding speech, that
|
||||
line is the difference between a known trade-off and a mystery.
|
||||
|
||||
## The probe
|
||||
|
||||
```bash
|
||||
uv run piecesai h3 render \
|
||||
--project-id <proj_id> --title "lang probe" --skill <calling-skill-name> \
|
||||
--prompt-file <path> --reference <anchor.png> \
|
||||
--h3-mode ref2va --duration 5.0 --megapixels 0.3
|
||||
```
|
||||
|
||||
Pick the shot with the most words per second, not the first shot. Listen for
|
||||
the words themselves, and check the mouth against them — a line can be
|
||||
intelligible and still be lip-synced to a different language's phonemes.
|
||||
|
||||
A failed probe is not a fleet problem. Do not load `h3-fleet-ops` for it.
|
||||
Go back to the user with what you heard and pick option 2 or 3.
|
||||
Reference in New Issue
Block a user