Compare commits

...
Author SHA1 Message Date
SolitaryThinker 49a13e6359 [skill] Add add-model and review-add-model-pr skills
Two skills split out of the will/stable-audio PR (#1260) so the model
work and the agent infra evolve on independent review cycles.

`add-model/SKILL.md` (~1600 lines) — the canonical procedure for
porting a new diffusion model into FastVideo, distilled from the Wan,
LTX-2, Hunyuan, Cosmos, Stable Audio, and MagiHuman ports. Covers the
17-row Files-table, 16-step procedure, FastVideo layer/attention
selection rules, parity-test pattern, weight conversion recipe, and
the I2V variant pattern.

`add-model/REVIEW.md` (~1900 lines) — 36 documented failure modes
discovered while porting / re-reviewing those families. Each item is
a self-contained "what we did wrong, what to do instead" entry with a
suggested action on the skill text. Items 1-22 from the Wan / MagiHuman
porting passes; items 23-36 from the Stable Audio port (audio as
first-class workload, hard ban on `from diffusers import` for model
classes at runtime, user-story example docstrings, single-class
kwargs-driven variants, examples accept paths not user-written decode
glue, etc.).

`add-model/add_model_split.md` — plan for breaking the monolithic
1600-line SKILL.md into ~12 satellite docs (deferred until after
the next porting pass validates which sections are most-referenced).

`review-add-model-pr/SKILL.md` (~480 lines, NEW) — reviewer-facing
dual of `add-model`. Walks a PR reviewer through the canonical
surface (per-component checklist for DiT / VAE / encoder /
PipelineConfig / pipeline class / stages / presets+registry / tests /
example), indexes the 36 REVIEW failure modes by where they show up
in a diff (the "Pitfall map"), and produces a structured verdict
(block / nit / follow-up) with a copy-pasteable comment skeleton.
Key design choice: the reviewer's job is NOT to redesign the port,
just to verify the porter followed the canonical procedure honestly
and exercised the parity gate — REVIEW item 16 ("skip-on-missing
parity is a silent no-op trap") gets disproportionate weight because
it's the single most expensive failure mode in past ports.

Both skills registered in `.agents/skills/index.jsonl` as
`status: draft, trust: low` per existing convention.
2026-04-26 17:14:49 -07:00
5 changed files with 3948 additions and 0 deletions
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+230
View File
@@ -0,0 +1,230 @@
# add-model skill — split plan
The current `SKILL.md` is **1605 lines** in a single file. This document
proposes splitting it into ~12 satellite docs with a much shorter
top-level index, following the idiomatic Anthropic-skills pattern of
"short procedural index + targeted satellites."
## Why split
1. **Cold-load cost.** The whole 1605-line file is loaded into the
model's context every time the skill fires. Most porting sessions
only need a fraction of that — e.g. a VAE-only contribution doesn't
need the I2V-variant section or the weight-conversion recipe. A
shorter `SKILL.md` index + lazy-loaded satellites keeps the live
context lean.
2. **Reviewability.** A single 1605-line markdown file is hard to
diff. Splits keep changes scoped — adding the "audio workload"
section (REVIEW item 25) becomes a new file in `how-to/` rather
than a 100-line insert in the middle of an existing megafile.
3. **Findability.** Section anchors in a single file are hard to
navigate; per-topic files surface in `ls .agents/skills/add-model/`
and `grep -r` returns clean per-file matches.
4. **Component-only contributions** (REVIEW item 23) become a
first-class workflow document instead of an awkward "but skip
half the steps" exception inside the main file.
## Current section inventory
| Section | Approx lines | Stays in SKILL.md? | Target file |
|---|---|---|---|
| Purpose | 8 | Yes | SKILL.md |
| When to use / not to use | 25 | Yes | SKILL.md |
| Prerequisites | 40 | Yes | SKILL.md |
| Inputs (table) | 15 | Yes | SKILL.md |
| FastVideo's single architecture | 40 | No | how-to/architecture.md |
| Files you will create or touch (table) | 25 | Yes (link to how-to) | SKILL.md + how-to/files_table.md |
| Steps (1–16) | 440 | **Index only** in SKILL.md (3-line per step + link) | how-to/steps_<phase>.md ×4 |
| Standard stages — subclass targets | 20 | No | how-to/architecture.md |
| FastVideo layers and attention | 110 | No | how-to/layers_and_attention.md |
| Parity test pattern | 165 | No | how-to/parity_testing.md |
| Parallel component porting | 100 | No | how-to/parity_testing.md |
| Weight conversion | 155 | No | how-to/weight_conversion.md |
| `register_configs` cheatsheet | 25 | No | how-to/registry_cheatsheet.md |
| Adding an I2V variant | 270 | No | how-to/i2v_variant.md |
| Distributed support | 35 | No | how-to/distributed_support.md |
| Common pitfalls | 50 | No | reference/pitfalls.md |
| Outputs | 15 | Yes | SKILL.md |
| Example prompt snippet | 35 | No | reference/example_prompt.md |
| References | 50 | No | reference/links.md |
| Changelog | 20 | No | reference/changelog.md |
## Target tree
```
.agents/skills/add-model/
├── SKILL.md ~250 lines — procedural index
├── REVIEW.md unchanged
├── add_model_split.md this doc
├── how-to/
│ ├── architecture.md ~70 lines — single-architecture diagram + standard stages
│ ├── files_table.md ~50 lines — annotated 17-row Files table
│ ├── steps_1_setup.md ~80 lines — steps 1–5 (gather, study, reuse, convert, clone)
│ ├── steps_2_components.md ~120 lines — step 6 + parallel component porting
│ ├── steps_3_pipeline.md ~100 lines — steps 7–11 (config, stages, pipeline class, presets, registry)
│ ├── steps_4_validation.md ~120 lines — steps 12–16 (smoke, pipeline parity, SSIM, cleanup, ask)
│ ├── parity_testing.md ~250 lines — conventions + component template + pipeline-level gate + subagent prompt
│ ├── weight_conversion.md ~200 lines — decision tree + recipe + reference scripts + gotchas
│ ├── layers_and_attention.md ~150 lines — linear/attention/primitive selection rules
│ ├── i2v_variant.md ~270 lines — full I2V add section verbatim (cleanly extractable)
│ ├── distributed_support.md ~50 lines — SP/TP/VAE-tiling rules
│ ├── registry_cheatsheet.md ~30 lines — register_configs fields
│ ├── component_only_contributions.md ~120 lines — NEW (REVIEW #23): VAE-only / encoder-only PR shape
│ └── audio_workload.md ~150 lines — NEW (REVIEW #25): audio output, T2A workload, no-SSIM metrics
├── reference/
│ ├── pitfalls.md ~80 lines — the 14-item pitfalls list
│ ├── example_prompt.md ~40 lines — the example user prompt snippet
│ ├── links.md ~50 lines — file/repo references
│ └── changelog.md ~30 lines — change history table
└── seed-ssim-references/ unchanged
```
Total: ~2200 lines across 17 files (vs current 1605 lines × 1 file).
The line growth is intentional — each file gets a short "Purpose +
Status + Prerequisites" header so it can be loaded without context
from the others.
## SKILL.md target shape (~250 lines)
```markdown
# Add a Model to FastVideo
## Purpose
[8 lines, unchanged]
## When to use / When not to use
[25 lines, unchanged]
## Prerequisites — gather inputs (blocking)
[40 lines, unchanged]
## Inputs
[15-line table, unchanged]
## Files you will create or touch
The full table lives in `how-to/files_table.md`. Quick sketch:
- Component files (model + config + __init__ export): rows 1–6.
- Pipeline files (config + class + stages + presets): rows 7–10.
- Registry: row 11.
- Tests (smoke + pipeline parity + per-component parity + SSIM): rows 12–15.
- Conversion script (only if not Diffusers format): row 16.
- Example: row 17.
For component-only contributions (just a VAE / encoder), see
`how-to/component_only_contributions.md` — you can skip rows 7–13 + 15
+ 17.
## Steps (procedural index)
1. **Gather inputs (blocking).** See `how-to/steps_1_setup.md`.
2. **Study the reference implementation.** See `how-to/steps_1_setup.md`.
3. **Decide what to reuse.** See `how-to/steps_1_setup.md`.
4. **Convert weights to Diffusers format.** Only if not Diffusers-format.
See `how-to/weight_conversion.md`.
5. **Clone the official repo for parity testing.** See `how-to/steps_1_setup.md`.
6. **Port components in parallel via subagents.** See
`how-to/steps_2_components.md` + `how-to/parity_testing.md`.
7. **Create the PipelineConfig.** See `how-to/steps_3_pipeline.md`.
8. **Build or pick the stages.** See `how-to/architecture.md` (standard
stages catalog) + `how-to/steps_3_pipeline.md`.
9. **Write the pipeline class.** See `how-to/steps_3_pipeline.md`.
10. **Define presets.** See `how-to/steps_3_pipeline.md`.
11. **Register in `fastvideo/registry.py`.** See `how-to/registry_cheatsheet.md`.
12. **Smoke-test the pipeline.** See `how-to/steps_4_validation.md`.
13. **Full-pipeline parity + example (gated).** See
`how-to/parity_testing.md` (the pipeline-level section is the
handoff gate).
14. **Add SSIM regression.** See `how-to/steps_4_validation.md`. For
audio, see `how-to/audio_workload.md` (no SSIM analog).
15. **Clean up the cloned reference repo.** See `how-to/steps_4_validation.md`.
16. **Ask about tests + perf data.** See `how-to/steps_4_validation.md`.
## Pre-handoff checklist
[New section addressing REVIEW item 16 — bans skip-only parity at handoff]
- [ ] `pytest tests/local_tests/<bucket>/test_<family>_*parity*.py -v` produces non-skip PASS for each non-reused component.
- [ ] `pytest tests/local_tests/pipelines/test_<family>_pipeline_parity.py -v` produces non-skip PASS.
- [ ] `python examples/inference/basic/basic_<family>.py` writes a non-corrupt mp4 (or .wav for audio).
- [ ] Conversion has actually been run (the parity tests skip if not).
## Outputs
[15 lines, unchanged]
## Common pitfalls
See `reference/pitfalls.md` for the full 14-item list. Most-cited:
- #1: `EntryClass` missing → pipeline silently invisible.
- #11: raw `nn.Linear` in DiT/VAE hot paths → use `ReplicatedLinear`.
- #16: skip-only parity → see pre-handoff checklist above.
## See also
- `reference/example_prompt.md` — example user prompt for invoking this skill.
- `reference/links.md` — file/repo references.
- `reference/changelog.md` — change history.
```
## Migration steps
1. **Create the new files** with content extracted from current
`SKILL.md` (no semantic edits — just relocation + cross-link edits).
2. **Add per-file headers** of the form:
```markdown
# <topic>
**Part of:** add-model skill
**When to read:** <one-liner — e.g. "during step 6 / parallel
component porting">
**Prerequisites:** <links to other files needed first>
---
<content>
```
3. **Rewrite SKILL.md** as the index. Each step gets the new 3-line
format: title + 2-line summary + link to `how-to/<file>.md`.
4. **Add the pre-handoff checklist** (addresses REVIEW item 16) — the
single load-bearing addition this split enables.
5. **Add `how-to/component_only_contributions.md`** (addresses REVIEW
item 23).
6. **Add `how-to/audio_workload.md`** (addresses REVIEW items 25 + 28).
7. **Update REVIEW.md** to mark items 16, 23, 25, 28 as resolved by
the split.
8. **Re-run a sample skill invocation** (e.g. on a hypothetical new
port) to validate the split — check that the model only loads
the index + 2-3 satellite files for a typical port.
## Risks / open questions
- **Breaks existing prompts.** Anyone with an in-flight skill
invocation may have memorized section anchors that move. Mitigate
by leaving anchor stubs in SKILL.md for one cycle (forwarding
comments).
- **Cross-link maintenance.** Each rename / move requires updating
cross-refs. Mitigate by keeping the file tree shallow (one
`how-to/` and one `reference/` directory only).
- **Discoverability of new files.** A porter who only reads
`SKILL.md` may not realize `audio_workload.md` exists. Mitigate by
having the index's step-3 line for "audio variant of step X" link
explicitly to the audio doc, and by keeping the "See also" section
visible.
- **What counts as "idiomatic"?** Anthropic skills tend toward
~150-300 line single-file or ~3-5 file splits. 17 files is on the
large side. We might collapse further if some satellites are
always-loaded-together (e.g. merge `architecture.md` +
`registry_cheatsheet.md` if they're never read independently).
Decide post-prototype.
## Next step
Implement the split as a separate PR (don't bundle with the
will/stable-audio first-class VAE work or the will/magi MagiHuman
port). Sequence:
1. Land the split as-is (mechanical relocation, zero semantic
change). Verify that running the skill against a known port
produces equivalent guidance.
2. Then layer in the REVIEW-item edits (16, 23, 25, 28) as content
changes inside the new structure.
+1
View File
@@ -6,3 +6,4 @@
{"name": "index-related-work", "description": "Ingest a paper or repository into the related work index", "path": "index-related-work/SKILL.md", "status": "draft", "trust": "low"}
{"name": "search-related-work", "description": "Query the related work index for relevant papers, repos, or comparisons", "path": "search-related-work/SKILL.md", "status": "draft", "trust": "low"}
{"name": "seed-ssim-references", "description": "Run a new or updated fastvideo/tests/ssim/ test on Modal, pull generated videos, and upload them to FastVideo/ssim-reference-videos so the test has a regression baseline", "path": "seed-ssim-references/SKILL.md", "status": "draft", "trust": "low"}
{"name": "review-add-model-pr", "description": "Review a PR that adds a new model / pipeline (or major variant) to FastVideo. Walks the canonical surface, indexes the 36 documented add-model failure modes by where they show up in a diff, and produces a structured verdict (block / nit / follow-up).", "path": "review-add-model-pr/SKILL.md", "status": "draft", "trust": "low"}
+482
View File
@@ -0,0 +1,482 @@
---
name: review-add-model-pr
description: Use when reviewing a PR that adds a new model / pipeline (or a major variant like I2V/V2V/DMD) to FastVideo under `fastvideo/pipelines/basic/<family>/`. Walks a reviewer through the canonical surface the porter should have touched, the parity bar, and the 36 documented failure modes from prior ports. Returns a structured review verdict (block / nit / follow-up).
---
# Review an add-model PR
## Purpose
Catch the failure modes that have actually shipped in past add-model PRs
(documented in `.agents/skills/add-model/REVIEW.md`) **before merge**,
without re-litigating design choices that the `add-model` skill has
already settled. The reviewer's job is *not* to redesign the port — it
is to verify the porter did the things the skill prescribes, exercised
the parity gate honestly, and surfaced the right knobs to end users.
This skill assumes the PR claims to add a model. For PRs adding only
sampling-preset tweaks, a single pipeline kwarg, or a new test against
an existing pipeline, this skill is overkill — comment scoped to those
specific changes instead.
## When to use
- A new pipeline directory under `fastvideo/pipelines/basic/<family>/`.
- A new variant pipeline (e.g. `<family>_i2v_pipeline.py`,
`<family>_dmd_pipeline.py`) sibling-added to an existing family.
- A "first-class component" PR that adds a new DiT, VAE, or encoder
port without (yet) wiring a pipeline (REVIEW item 23).
- Re-review of a port that previously skipped the post-parity hot-path
pass (REVIEW item 33) or shipped with placeholder diffusers imports
(REVIEW item 30).
## When not to use
- PR only edits sampling defaults in an existing
`<family>/presets.py` → review the values against the model card
inline, no skill needed.
- PR only adds a new SSIM reference → use the
`seed-ssim-references` skill instead.
- PR refactors shared infra (`fastvideo/layers/`, `fastvideo/attention/`,
`fastvideo/pipelines/stages/` base classes) without touching a
family directory → that's not an add-model PR.
## Required reading before starting the review
1. **The PR description.** What family is being added, what variants
(T2V / I2V / V2V / DMD / T2A / …), what the porter claims is parity-
verified, and which of the four prereqs (official repo URL, HF
weights path, HF token, target `model_family`) they collected.
2. **`.agents/skills/add-model/SKILL.md` Files-table** (rows 1–17) — the
canonical surface a model port touches. You'll cross-reference this
against the diff in step 2 below.
3. **`.agents/skills/add-model/REVIEW.md` summary table** — the 36
failure modes. The "Pitfall map" section below indexes them by where
they typically show up in a diff.
## Inputs
| Parameter | Required | Description |
|-----------|----------|-------------|
| `pr` | Yes | GitHub PR number, URL, or local branch name. |
| `model_family` | Recommended | The snake_case family slug (e.g. `stable_audio`, `magi_human`). Lets you grep-scope the review. |
| `official_repo` | Recommended | URL of the upstream reference, so you can sanity-check the parity test references it correctly. |
| `parity_run_log` | Optional | If the porter attached a parity run log, read it before reading code. |
## Steps
### 1. Verify the PR scope is shaped like an add-model PR
Run `gh pr view <pr> --json files | jq -r '.files[].path'` (or
`git diff --name-only origin/main...HEAD`). Confirm the diff touches
**at least 6 of the 17 Files-table rows** in `add-model/SKILL.md`.
Typical shape:
| If you see... | Expect... | If missing... |
|---|---|---|
| `fastvideo/models/dits/<family>.py` | `fastvideo/configs/models/dits/<family>.py` + export in `__init__.py` | Block: DiT config + export missing (Files-table rows 2 + 3). |
| `fastvideo/pipelines/basic/<family>/<family>_pipeline.py` | `presets.py`, `registry.py` edit, smoke test, parity test, example | Block on whichever is missing — the porter will be told the same by the skill. |
| `<family>_i2v_pipeline.py` (sibling-added) | A new preset for the I2V workload + the registry entry pointing at the I2V HF repo | Block: I2V variants get their own preset + workload tag (REVIEW item 8). |
| Standalone DiT/VAE/encoder under `fastvideo/models/<bucket>/` with no pipeline | A "first-class component" PR (REVIEW item 23) | Confirm with the author that the consuming pipeline PR is in flight or planned. Don't block. |
| Edits under `fastvideo/configs/pipelines/base.py`, `fastvideo/api/sampling_param.py`, `fastvideo/pipelines/pipeline_batch_info.py` | New pipeline-call kwargs the family needs | Sanity-check the field's default doesn't change behavior for other families. Often a footgun (e.g. `0 or default` Python truthiness). |
### 2. Walk the diff in dependency order, not file order
Components have a strict dependency order — review them in this order
so you can spot mismatches as they cascade:
1. **DiT** (`fastvideo/models/dits/<family>.py` + config) — foundational.
2. **VAE** — only one usually exists; if not, this is the second-biggest
review surface.
3. **Encoder(s) / conditioner** — text encoder is usually shared; new
conditioners (e.g. `MultiConditioner`-style) need careful look.
4. **`PipelineConfig`** subclass.
5. **Pipeline class** — wires modules + stages.
6. **Stages** — most should be standard; mod-specific subclasses
warrant careful review.
7. **Presets + registry** — usually mechanical; check workload type
selection against REVIEW item 28.
8. **Tests** — both component parity and pipeline parity.
9. **Example script** — last, but the bar is high (REVIEW items 31, 35).
For each component, run the Per-component checklist (next section).
### 3. Apply the Per-component checklist
For each new file in `fastvideo/models/<bucket>/<family>.py`:
#### DiT review (`fastvideo/models/dits/<family>.py`)
- [ ] **No raw `nn.Linear`** in QKV/MLP/proj/embedder paths. All
projections should be `ReplicatedLinear` from
`fastvideo.layers.linear`. Exceptions are the legacy MoE-packed
case (REVIEW item 11) — must be flagged in a comment with the
weight-layout justification.
- [ ] **No raw SDPA / `flash_attn_func`** calls. Self-attention should
be `DistributedAttention` (or `LocalAttention` for cross-attention).
See `add-model/SKILL.md` "Attention layers" table.
- [ ] **No raw `nn.LayerNorm` in modulation paths.** Should be
`FP32LayerNorm` / `RMSNorm` / `LayerNormScaleShift` from
`fastvideo.layers.layernorm`.
- [ ] **No `from diffusers import` or `from transformers import
<ModelClass>`** anywhere outside of test files (REVIEW item 30, the
hard ban). Tokenizers are the *only* allowed `from transformers`
runtime import in production code.
- [ ] **`from_official_state_dict()` (or equivalent)** is provided so
the consuming pipeline can load the published checkpoint without
going through `diffusers.from_pretrained`.
- [ ] **Param-mapping deviations from upstream are localized** — e.g.
if the model uses `gamma`/`beta` and FastVideo's `FP32LayerNorm`
uses `weight`/`bias`, the remap happens *in the loader*, not by
renaming the layers.
- [ ] **Partial / non-standard rotary** (e.g. halves-swap vs
interleaved-pair) is kept local with a one-line WHY comment, not a
copy of FastVideo's `_apply_rotary_emb` with edits.
#### VAE review (`fastvideo/models/vaes/<family or arch>.py`)
- [ ] **File name matches the convention** (REVIEW item 29): name
after the *arch* if the VAE is shared across families
(`oobleck.py`, `autoencoder_kl.py`); name after the *family* if it's
specific (`wanvae.py`).
- [ ] **`from_pretrained()` (or `from_official_state_dict`)** loads
weights from the published HF path *without* a Diffusers
intermediary (REVIEW item 30 again).
- [ ] **Per-channel `latents_mean`/`latents_std` are reshaped with
explicit `.view(1, -1, 1, 1, 1)`** when applied (REVIEW item 22) —
silent broadcasting along the wrong dim is a stealth bug.
- [ ] **Normalization-convention mismatches with the wrapper**
(REVIEW item 20) — if upstream `decode()` does
`z = z*std + mean` *internally* but the FastVideo wrapper expects
pre-denormalized input, the parity test must compensate or the
decode parity will look broken.
- [ ] **Pipeline-glue wrapper** (e.g. `fastvideo/models/vaes/<family>_audio.py`
for lazy-load semantics) exists if the pipeline needs lazy-load /
hide-from-named_parameters semantics (REVIEW item 26).
#### Conditioner / encoder review
- [ ] If the conditioner mixes text + numeric (e.g. duration), it
produces the **DiT-ready (cross_attn_cond, cross_attn_mask,
global_embed) triple** in a single helper, not via inline cat-ing
in the conditioning stage.
- [ ] **T5 / Llama / SigLIP TP wiring** uses the encoder bucket's TP
primitives (`QKVParallelLinear`, `MergedColumnParallelLinear`,
`RowParallelLinear`) — not `ReplicatedLinear`.
- [ ] If the conditioner intentionally hides its T5 from
`named_parameters()` (so the SA-style checkpoint loader doesn't try
to match upstream-absent T5 keys), that exclusion is documented in
a comment.
#### `PipelineConfig` review (`fastvideo/configs/pipelines/<family>.py`
or `fastvideo/pipelines/basic/<family>/pipeline_configs.py`)
- [ ] **Subclasses `PipelineConfig`** from
`fastvideo.configs.pipelines.base`.
- [ ] **`vae_config`, `dit_config`, `text_encoder_configs`** are
defaulted via `field(default_factory=...)` (mutable-default-arg
Python rule).
- [ ] **The text-encoder slot** matches what the pipeline actually
uses. If the pipeline owns its own conditioner (no FastVideo-loaded
text encoder), the text-encoder tuples should be `tuple()` and the
parent's length-equality validator should still pass — the porter
must zero out *all four* of `text_encoder_configs`,
`text_encoder_precisions`, `preprocess_text_funcs`,
`postprocess_text_funcs` together (we've seen this break before).
- [ ] **Component-bucket inheritance** (REVIEW item 24) — if the new
config goes under `vaes/`, it must subclass `VAEConfig` not
`EncoderConfig`. The bucket directory determines the base.
- [ ] **`__post_init__`** is used only to flip `load_encoder` /
`load_decoder` / etc. on the child configs, not to do any heavy
build.
#### Pipeline class review (`fastvideo/pipelines/basic/<family>/<family>_pipeline.py`)
- [ ] **`EntryClass` is a single class**, not a list.
- [ ] **`_required_config_modules`** lists exactly what the loader
reads from `model_index.json`. Missing keys cause silent loading
degradation.
- [ ] **`load_modules()`** does not have any `from diffusers import`
/ `from transformers import <ModelClass>` for production
components. If the porter explicitly opted into a temporary
diffusers shim, REJECT — the right move is to ship the native port
or hold the pipeline back (REVIEW item 30).
- [ ] **`torch.backends.*` flags** (TF32, cuDNN benchmark) — if set,
they're set **once in `load_modules`**, not per-call inside a stage
(REVIEW item 33). Mid-run flips invalidate the cuDNN algorithm
cache and amplify A2A SDE drift.
- [ ] **`create_pipeline_stages()`** uses standard stages from
`fastvideo.pipelines.stages` where possible, only subclassing when
the math diverges. New stage classes live in
`fastvideo/pipelines/basic/<family>/stages/` and are re-exported in
`fastvideo/pipelines/stages/__init__.py`.
- [ ] **One pipeline class for kwargs-driven variants** (REVIEW item
34) — T2A/A2A/inpaint that share weights/components shouldn't be
split into three classes. Triggers to split: separate
`_required_config_modules`, separate HF repo, divergent forward
signatures, separate `WorkloadType`. Reject the split unless one
of those applies.
#### Stages review (`fastvideo/pipelines/basic/<family>/stages/*.py`)
- [ ] **No `init_audio_strength = 0` / "0 or default" footguns** —
Python truthy-or fallbacks (`x or default`) silently swallow `0`,
`0.0`, `""`. Use `x if x is not None else default`.
- [ ] **Loud-fail on malformed kwarg combos** — e.g. inpaint without
mask should raise `ValueError`, not silently fall through to T2A.
- [ ] **Hot-path discipline** (REVIEW item 33): no per-step
`torch.zeros_like(...)` or `torch.randn_like(...)` inside the
sampler loop; pre-allocate buffers + reuse via `.normal_()` /
`.copy_()`.
- [ ] **No dead `batch.extra` writes** — every key written should be
read by a downstream stage.
- [ ] **Stage docstring** is one or two lines. No "Mirrors upstream X"
/ "Vendored from Y" provenance (REVIEW item 36); upstream
comparison belongs in the parity test, not the production docstring.
#### Presets + registry review
- [ ] **Sampling defaults** match the published model card example
block. Track the *model* defaults, not the upstream library's
*generic* defaults (this distinction shipped a 100% drift before
for Stable Audio).
- [ ] **`workload_type`** matches what the model actually does. If
the family is audio (T2A/A2A/AV) and `WorkloadType` doesn't yet
have those values (REVIEW item 28), accept `"t2v"` as a placeholder
but require a TODO comment + the porter to file a follow-up.
- [ ] **Registry detector** matches both the HF path and the
pipeline class name (`_class_name` from `model_index.json`).
- [ ] **`ALL_PRESETS`** export exists and is added to
`_register_presets()`'s group tuple in `fastvideo/registry.py`.
#### Tests review — **the most load-bearing review surface**
REVIEW item 16 is the single most expensive failure mode. A skipped
test reads as green in CI. Confirm explicitly:
- [ ] **Component parity tests exist** for every non-reused
component: DiT, VAE (if new), encoder (if new). Find them under
`tests/local_tests/<bucket>/test_<family>_*.py`.
- [ ] **Each component parity test produces a non-skip pass on the
reviewer's machine** if at all possible. If the porter says "I ran
it locally", ask for the diff numbers in the PR description.
- [ ] **Pipeline parity test exists** at
`tests/local_tests/pipelines/test_<family>_pipeline_parity.py` AND
has a non-skip pass — *not* a "skipped because the official clone
is missing" pass.
- [ ] **Smoke test exists** at `test_<family>_pipeline_smoke.py` — no
GPU, just import + registry + preset wiring. CI can run this even
if local-only parity tests skip.
- [ ] **Parity reference is the official upstream**, not diffusers
(REVIEW item 30). Diffusers parity is acceptable as a *secondary*
test only when (a) the published weights load through both and (b)
the official repo is also imported and compared.
- [ ] **Tolerances are scope-appropriate** (REVIEW item 21): single-
block + single-kernel = `atol=1e-4`; full-DiT cross-kernel = `0.1`
with a complementary `abs_mean drift < 5%` check; bare `assert_close`
alone with a loose tolerance is a smell.
- [ ] **Stub helpers for upstream private DSL deps** (REVIEW items
17, 18, 32) — if the upstream has `magi_compiler`-style imports
that aren't on PyPI, a small `tests/local_tests/helpers/<family>_upstream.py`
shim is fine. But if the deps it bypasses become real installs,
the shim must be deleted (item 32 — no zombie no-op shims).
- [ ] **GQA-aware kernel routing in stub paths** (REVIEW item 19) —
if the parity test routes upstream's flash_attn through SDPA, KV
heads must be `repeat_interleave`'d explicitly.
- [ ] **VAE normalization symmetry** (REVIEW item 20) — if upstream
bundles `z = z*std + mean` in `decode()` and FastVideo expects
pre-denormalized, the test must compensate explicitly.
#### Example script review (`examples/inference/basic/basic_<family>*.py`)
The bar here is high — the example is the user's entry point, not the
porter's debugging script.
- [ ] **User-story-shaped docstring** (REVIEW item 31) — at least one
`User story (<persona>):` block, then a "How it works" / "Picking
the dial" / "Tunable knobs" section. Not a code-narration docstring.
- [ ] **5-15 LOC of constants + one `generate_video()` call**
(REVIEW item 35). If the example carries a 25-line `_load_reference`
/ shape-norm / resample helper, that glue belongs *in the
pipeline*, not the example.
- [ ] **Pipeline accepts file paths** for any media-input kwarg
(`init_audio`, `inpaint_audio`, `image_path`). The example just
passes the path; pipeline does decode + resample internally.
- [ ] **No `torchaudio.load` on container formats** (mp4, m4a) —
routes through `torchcodec` → CUDA NVRTC. PyAV (already a
FastVideo dep, used by `_mux_audio`) handles all formats.
- [ ] **Tunable-knob defaults** match the model's published sweet
spot, not the upstream library's generic defaults.
#### Local repro doc review (optional but recommended)
If the family ships a `tests/local_tests/<family>.md`:
- [ ] **Setup section** covers HF gated access, optional inference
deps, upstream clone instructions, model cache pre-warm.
- [ ] **Per-test table** explains what each test compares against
with expected drift numbers.
- [ ] **Troubleshooting section** covers gated-skip behavior, batch-
vs-single-run flag interactions (cuDNN benchmark!), first-call
cache download blowup.
### 4. Cross-check the "first-class component" rule
Run `grep -rn "from diffusers import\|from transformers import"
fastvideo/pipelines/basic/<family>/ fastvideo/models/dits/<family>*
fastvideo/models/vaes/<family>* fastvideo/models/encoders/<family>*`.
The **only** acceptable hits are `from transformers import
<TokenizerFast>` (data-utility, no weights) and `from transformers
import T5EncoderModel` *only if* (a) it's loaded via HF and (b) the
T5 weights are absent from the model's checkpoint by design (e.g.
SA conditioner).
Anything else — `StableAudioDiTModel`, `AutoencoderKL`,
`UnetXxx`, `T2VPipeline` — is a REVIEW item 30 violation. Block
the PR with a pointer to that item.
### 5. Run the parity tests yourself if budget permits
The porter said it passes. Confirm:
```bash
# DiT/VAE/encoder component parity
pytest tests/local_tests/<bucket>/test_<family>_*.py -v -s
# Pipeline parity (the gate per add-model step 13(a))
pytest tests/local_tests/pipelines/test_<family>_pipeline_parity.py -v -s
# Smoke (no GPU)
pytest tests/local_tests/pipelines/test_<family>_pipeline_smoke.py -v
```
If any *parity* test SKIPs rather than PASSes on your machine and
you have the prerequisites set up, that means the porter never
actually verified parity (REVIEW item 16). Block.
### 6. Read REVIEW.md for any new failure modes the porter didn't address
The PR may also amend REVIEW.md with newly-discovered failure modes.
Skim those — they're typically the most accurate signal of what to
look for in *this specific* port. If the porter added a REVIEW item
and the linked code in the PR doesn't actually mitigate that item,
that's a contradiction — flag it.
### 7. Write the verdict
Structure your review comment as three buckets:
- **Block (must fix before merge)** — REVIEW item 30 violations,
skipped parity tests, missing required Files-table rows, dead
`batch.extra` writes that hide caller bugs, footguns like `0 or
default`.
- **Nit (would be better)** — naming convention drift, narrative
comments per REVIEW item 36, missing user-story docstrings,
hot-path allocations that don't change correctness.
- **Follow-up (not this PR)** — extracting test boilerplate to a
shared helper, future variant pipelines, performance benchmarks.
Always link each item back to the `add-model/REVIEW.md` item number
when applicable; that's the institutional memory and lets the porter
fix the issue with full context.
## Pitfall map (REVIEW.md items by where they show up in a diff)
Use this when you've spotted something off and want to find the
documented failure mode it corresponds to.
| If the diff has... | Suspect REVIEW item(s) |
|---|---|
| `from diffusers import <ModelClass>` in `fastvideo/...` | **30** (hard ban) |
| Raw `nn.Linear` in DiT projections | 10, 11 (only acceptable for MoE-packed weights with comment) |
| Custom RoPE that's *not* `_apply_rotary_emb` | Probably fine if the convention differs (e.g. halves-swap), but require a one-line WHY comment + a parity-test row |
| `parity_test.py` that calls `pytest.skip` unconditionally | **16** (silent no-op trap) — block |
| Component parity tests missing | **16** — block |
| New pipeline file with no `register_configs` call in registry.py | Files-table row 11 missing |
| `_required_config_modules` that doesn't match `model_index.json` keys | Silent loading degradation; block |
| `text_encoder_configs=tuple()` but other text-encoder tuples non-empty | Length-equality validator will fail at runtime |
| ArchConfig has `num_inference_steps` / `guidance_scale` / `flow_shift` | **15a** — pipeline-level fields leaking into ArchConfig; block |
| `<family>vae.py` for an arch-shared VAE (e.g. Oobleck) | **29** — should be `<arch>.py` (e.g. `oobleck.py`) |
| Pipeline imports `from transformers import T5EncoderModel` | OK only if the conditioner intentionally hides T5 from `named_parameters()` and the checkpoint omits T5 keys; document the exception |
| `torch.backends.*` set in a stage's `forward()` | **33** — should be one-shot in `load_modules` |
| `init_X = 0` silently treated as default via `or` | **33-adjacent** — Python truthy footgun; flag |
| Per-step `torch.zeros_like` / `randn_like` in sampler callback | **33** — pre-allocate; can also fix accuracy regressions |
| Magic `_DOWNSAMPLING_RATIO = 2048` in stage code | **33** — derive from the VAE config |
| `examples/.../basic_<family>*.py` >30 LOC | **35** — decode/resample/shape-norm belongs in the pipeline |
| Example uses `torchaudio.load(...)` on mp4 | **35** — torchcodec → NVRTC dep chain; use PyAV |
| Example docstring narrates code | **31** — needs `User story (<persona>):` block |
| Pipeline class docstring narrates upstream provenance | **36** — strip "Vendored from X" / "Mirrors upstream Y" |
| Comment says "previously this was X, we moved it because Y" | **36** — strip; goes in commit message, not code |
| Diff adds `tests/local_tests/helpers/<family>_upstream.py` that's a no-op | **32** — delete the no-op shim + its call sites |
| Diff adds `init_audio` / `inpaint_audio` / similar to `SamplingParam` | OK; verify they default to `None` and other families ignore |
| Multiple pipeline classes that share weights/components | **34** — should be one class with kwargs-driven modes |
| Config under `fastvideo/configs/models/encoders/` for a VAE | **24** — wrong bucket; should be under `vaes/` with `VAEConfig` base |
| New pipeline that depends on a not-yet-ported component (placeholder import) | **30** — hold the pipeline back until the component is ported |
## Outputs
A structured review comment on the PR with:
1. **Verdict** — `approve` / `request-changes` / `comment-only`.
2. **Block list** — REVIEW-item-linked issues that must be fixed.
3. **Nit list** — style / convention drift.
4. **Follow-up list** — items the porter should know about but
shouldn't fix in this PR.
5. **Parity numbers you confirmed** — diff_max / diff_mean / drift /
element-wise bound, per parity test you ran. Lets the next reviewer
skip re-running.
## Example review comment skeleton
```
## Review summary
**Verdict:** request-changes
I ran the smoke + DiT-component parity tests and read the full diff.
The native ports look right; two REVIEW-30 violations in the pipeline
file need resolving before merge, plus a few smaller nits.
### Block (must fix)
- `fastvideo/pipelines/basic/<family>/<family>_pipeline.py:NN` — `from
diffusers import <ModelClass>` violates REVIEW item 30 (hard ban).
Either ship the first-class port now or hold this pipeline back
until the component is ported.
- `tests/local_tests/pipelines/test_<family>_pipeline_parity.py` skips
on my machine even with HF token set (the upstream-clone path check
fails). REVIEW item 16: a parity test that always skips is worse
than no test. Update the path resolution or document it in
`tests/local_tests/<family>.md`.
### Nit
- `examples/inference/basic/basic_<family>.py` docstring is code-narration;
REVIEW item 31 wants a `User story (<persona>):` block.
- `<family>_pipeline.py:NN` has a per-call `torch.backends.cuda.matmul.allow_tf32 = False`;
REVIEW item 33 says move to one-shot in `load_modules`.
### Follow-up
- HF-token boilerplate is duplicated across 4 parity files. REVIEW
item 1-tier work, but doesn't need to land in this PR.
### Parity numbers I confirmed locally
| Test | diff_max | diff_mean | drift |
|---|---|---|---|
| DiT component parity | 0.0 | 0.0 | bit-identical |
| VAE decode parity | 0.0 | 0.0 | bit-identical |
| Pipeline parity (T2V, 25 steps) | 0.012 | 0.0009 | 0.31% |
```
## References
- `.agents/skills/add-model/SKILL.md` — the canonical procedure;
every "should be there" claim in this skill is a row in that
skill's Files-table.
- `.agents/skills/add-model/REVIEW.md` — 36 documented failure modes;
the Pitfall map above indexes them by where they show up in a diff.
- `.agents/skills/seed-ssim-references/SKILL.md` — for SSIM regression
add-on PRs (separate from this skill's scope).