T32: moderateFaceBlacklist(imagePath, assetId) — same pluggable seam
shape as T26, for mode 3's uploaded face photos (需求文档 5 节: filtering
politically-sensitive likenesses). Default-permissive (auto_pass) when
unconfigured, per explicit user confirmation this session — deliberately
not assumed, since a wrong-default here (a real face silently passed
through) has a different risk profile than T26's garment photos.
T33: forwardToFalAssets (uploads.js) + fal's queue API (falSubmit/
falPollStatus/falFetchResult) wired into submitGeneration/
generateDanceVideo, which now dispatches on job.backend between the
existing H3 Console path (renamed generateDanceVideoViaH3, unchanged
behavior) and the new generateDanceVideoViaFal. A paid request (userId
set) routes to fal once FAL_API_KEY is configured; everything else —
free mode-一, or paid but no key yet — stays on H3 Console exactly as
before (verified: full suite green, no FAL_API_KEY set in any existing
test's env).
Endpoints/params (storage/upload/initiate, queue.fal.run/{model}/
requests/{id}/status vs /requests/{id} as two separate calls,
Authorization: Key <FAL_API_KEY>) are taken from fal's published docs
and the fal-js client's own source, not guessed — fal-generation.test.js
proves this codebase's side of the integration against a mock fal
server. What remains genuinely unverified (no FAL_API_KEY exists) is
whether the H3-Console-tuned prompt is interpreted the same way by
fal's minimax/h3/reference-to-video model, and fal's exact
terminal-failure status string(s) — flagged in code comments rather
than guessed at.
Also fixes a real gap T27 shipped with: submitGeneration never received
userId, so every mode-2 clip's requestedByUserId (M.7) was silently
null. T27's call site now passes userId (priority stays "normal" —
only mode 3 skips voting), which also makes mode 2 fal-eligible.
dance-mode2.test.js's happy-path test now asserts this.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011DUiYs6UjxDMkfvnjtsKFo