@revola-ai/video-craft 0.2.1

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.
Files changed (340) hide show
  1. package/README.md +56 -0
  2. package/dist/anchors.js +69 -0
  3. package/dist/corpus.js +91 -0
  4. package/dist/projectDoc.js +83 -0
  5. package/dist/projectDocSync.js +39 -0
  6. package/dist/projectMeta.js +37 -0
  7. package/dist/provenance.js +41 -0
  8. package/dist/radar-check-ids.json +34 -0
  9. package/dist/registry.js +307 -0
  10. package/dist/rules/animatedBarChartTightBox.js +15 -0
  11. package/dist/rules/animatedBarChartWhenNotToUse.js +15 -0
  12. package/dist/rules/animatedBarChartWhenToUse.js +15 -0
  13. package/dist/rules/animatedLineChartWhenNotToUse.js +15 -0
  14. package/dist/rules/animatedLineChartWhenToUse.js +15 -0
  15. package/dist/rules/approvedSfxPaletteOnly.js +15 -0
  16. package/dist/rules/atmosphereGradientsFadeAtBothEnds.js +15 -0
  17. package/dist/rules/audioIsTheClock.js +16 -0
  18. package/dist/rules/beatsDeriveFromTheirOwnSequence.js +15 -0
  19. package/dist/rules/bestSceneSetsTheBar.js +15 -0
  20. package/dist/rules/bumpBeforeRefreeze.js +15 -0
  21. package/dist/rules/cameraIsTheNarrator.js +15 -0
  22. package/dist/rules/checkPinBeforeShip.js +16 -0
  23. package/dist/rules/coldViewerClarity.js +15 -0
  24. package/dist/rules/commitOrCutEffects.js +29 -0
  25. package/dist/rules/convertFramesAcrossClockBoundary.js +15 -0
  26. package/dist/rules/crossCutElementsLiveInTheHudLayer.js +15 -0
  27. package/dist/rules/cursorClicksRealControls.js +15 -0
  28. package/dist/rules/cutOnRealAudioPauses.js +15 -0
  29. package/dist/rules/darkButNeverDim.js +15 -0
  30. package/dist/rules/dataRadarTagsDeclared.js +193 -0
  31. package/dist/rules/declareAVerticalZoneMap.js +15 -0
  32. package/dist/rules/deleteRejectedRigsCompletely.js +15 -0
  33. package/dist/rules/deriveSeamsDontMeasure.js +17 -0
  34. package/dist/rules/deterministicSceneSource.js +65 -0
  35. package/dist/rules/diegeticSoundForOnscreenActions.js +15 -0
  36. package/dist/rules/distinctTransitionPerBeat.js +15 -0
  37. package/dist/rules/downloadOrDrawRichAssets.js +15 -0
  38. package/dist/rules/elementOnscreenForItsVoSentence.js +17 -0
  39. package/dist/rules/emitBuildNotes.js +15 -0
  40. package/dist/rules/energyFromTimingNotPartCount.js +15 -0
  41. package/dist/rules/evidenceBeforeClaimingFixed.js +15 -0
  42. package/dist/rules/exactPeerLockstep.js +15 -0
  43. package/dist/rules/firstPassIsADraft.js +15 -0
  44. package/dist/rules/fixCrowdingByRebandingNotNudging.js +15 -0
  45. package/dist/rules/fixesPreserveApprovedElements.js +15 -0
  46. package/dist/rules/floatingNotBoxed.js +15 -0
  47. package/dist/rules/focusBeatsRecedeTheContext.js +15 -0
  48. package/dist/rules/fontVarsBrandRemocn.js +15 -0
  49. package/dist/rules/geometricSeamsNotCrossfades.js +23 -0
  50. package/dist/rules/geometrySidecarExportsTyped.js +36 -0
  51. package/dist/rules/glowIsAFocalAccent.js +22 -0
  52. package/dist/rules/heroFillsTheFrame.js +17 -0
  53. package/dist/rules/htmlMasksForAnimatedCounters.js +15 -0
  54. package/dist/rules/impactStackOnPayoffs.js +15 -0
  55. package/dist/rules/interactiveTargetsAreDeclaredData.js +15 -0
  56. package/dist/rules/interpolateLiteralKeysMonotonic.js +79 -0
  57. package/dist/rules/latenessIsStructuralOverlapTheBeats.js +15 -0
  58. package/dist/rules/lessTextMoreVisualizations.js +15 -0
  59. package/dist/rules/liveLinkDuplicateReactAlias.js +15 -0
  60. package/dist/rules/make2dElements3d.js +15 -0
  61. package/dist/rules/masterMixLoudnessTarget.js +17 -0
  62. package/dist/rules/measureAlignmentDontEyeball.js +15 -0
  63. package/dist/rules/motionCarriesWeightAndMeaning.js +15 -0
  64. package/dist/rules/motionDensityFloor.js +15 -0
  65. package/dist/rules/neverEditTierBVendored.js +15 -0
  66. package/dist/rules/neverRestateDerivedFrameNumbersInComments.js +15 -0
  67. package/dist/rules/noAiGradientLook.js +15 -0
  68. package/dist/rules/noColoredFlashToHideACut.js +22 -0
  69. package/dist/rules/noDoubleFrameShift.js +126 -0
  70. package/dist/rules/noDuplicateMotionPrimitives.js +15 -0
  71. package/dist/rules/noLinearMotion.js +22 -0
  72. package/dist/rules/noNestedSvgFilters.js +93 -0
  73. package/dist/rules/noRepeatedAssetAcrossShots.js +15 -0
  74. package/dist/rules/noShorthandWithUndefinedLonghand.js +93 -0
  75. package/dist/rules/noStackedFadesAtACut.js +24 -0
  76. package/dist/rules/noStaticHolds.js +17 -0
  77. package/dist/rules/occlusionVisibleElements.js +17 -0
  78. package/dist/rules/oneContinuousVoTrack.js +15 -0
  79. package/dist/rules/onePunchPerMomentClass.js +15 -0
  80. package/dist/rules/oneRenderPerRound.js +15 -0
  81. package/dist/rules/openStaticAfterAHardCut.js +22 -0
  82. package/dist/rules/openingsAndClosingsCarryMore.js +15 -0
  83. package/dist/rules/persistentDeviceFrames.js +15 -0
  84. package/dist/rules/pixelDiffAnEmptyBandFirst.js +15 -0
  85. package/dist/rules/planIsPublishedNotGating.js +15 -0
  86. package/dist/rules/poweredByRevolaWatermark.js +15 -0
  87. package/dist/rules/predrawContentPresentAtTheCut.js +17 -0
  88. package/dist/rules/premiumSurfacesNotFlatBorders.js +15 -0
  89. package/dist/rules/radarDispositionsAreHumanOnly.js +17 -0
  90. package/dist/rules/realAssetsNotPlaceholders.js +15 -0
  91. package/dist/rules/rebuildUiFromReconNotScreenshots.js +15 -0
  92. package/dist/rules/reconStrandsRunInParallel.js +15 -0
  93. package/dist/rules/registryBeforeHandrolling.js +15 -0
  94. package/dist/rules/remapRateEndpointFormula.js +15 -0
  95. package/dist/rules/remocnContentTailwindChrome.js +15 -0
  96. package/dist/rules/retimeOnlyViaPlaybackOrDesignTokens.js +15 -0
  97. package/dist/rules/reviewTheRenderByFrameSweep.js +15 -0
  98. package/dist/rules/rollingNumberBoundedSequence.js +17 -0
  99. package/dist/rules/rollingNumberSequenceLayoutNone.js +16 -0
  100. package/dist/rules/rollingNumberTightBoxAlignment.js +15 -0
  101. package/dist/rules/rollingNumberWhenNotToUse.js +17 -0
  102. package/dist/rules/rollingNumberWhenToUse.js +17 -0
  103. package/dist/rules/sameFamilySameProject.js +15 -0
  104. package/dist/rules/sampleBrandFromProduct.js +15 -0
  105. package/dist/rules/sampleVelocityFromPreviousFramePosition.js +15 -0
  106. package/dist/rules/sceneRootTagDeclared.js +41 -0
  107. package/dist/rules/scrubDemoDataToGenericRoles.js +15 -0
  108. package/dist/rules/selfReviewThePlanForGaps.js +15 -0
  109. package/dist/rules/sourceArchivedBeforeBrief.js +17 -0
  110. package/dist/rules/statisticsSelfExplanatory.js +15 -0
  111. package/dist/rules/stopsAsRelativeSpacing.js +15 -0
  112. package/dist/rules/theWholeCanvasWorks.js +15 -0
  113. package/dist/rules/themePerSection.js +22 -0
  114. package/dist/rules/thesisDeclaredInBrief.js +17 -0
  115. package/dist/rules/tierARequiresPropUnreachableEdit.js +15 -0
  116. package/dist/rules/tinyBodyCopyAsRuledLines.js +15 -0
  117. package/dist/rules/titlesPinnedOutsideCameraPush.js +15 -0
  118. package/dist/rules/tokensNotRawHexes.js +60 -0
  119. package/dist/rules/typeTokenDefaultedParamsAsString.js +15 -0
  120. package/dist/rules/varyKitChoreographyAcrossSections.js +15 -0
  121. package/dist/rules/varyTheVisualizationGrammar.js +15 -0
  122. package/dist/rules/verifyCheapRenderDear.js +15 -0
  123. package/dist/rules/voAddressesTheViewer.js +15 -0
  124. package/dist/rules/voIsAPerformance.js +15 -0
  125. package/dist/rules/voScriptLocksSpokenWords.js +15 -0
  126. package/dist/rules/wrappersPreserveChildGeometry.js +15 -0
  127. package/dist/sourceCheck.js +47 -0
  128. package/dist/sourceCitations.js +68 -0
  129. package/dist/stageProjection.js +69 -0
  130. package/dist/stages.js +163 -0
  131. package/dist/testSupport.js +6 -0
  132. package/dist/types.js +0 -0
  133. package/dist/variants.js +8 -0
  134. package/dist/waiverProjection.js +20 -0
  135. package/package.json +32 -0
  136. package/src/anchors.test.ts +119 -0
  137. package/src/anchors.ts +110 -0
  138. package/src/corpus.test.ts +96 -0
  139. package/src/corpus.ts +149 -0
  140. package/src/corpusProject.test.ts +103 -0
  141. package/src/creativeEdge.test.ts +58 -0
  142. package/src/distBuild.test.ts +86 -0
  143. package/src/docStatus.test.ts +81 -0
  144. package/src/drift.test.ts +124 -0
  145. package/src/fixtures/data-radar-dynamic/Branches.tsx +14 -0
  146. package/src/fixtures/data-radar-dynamic/plan.fixture.yaml +16 -0
  147. package/src/fixtures/data-radar-forwarded/Scene.tsx +19 -0
  148. package/src/fixtures/data-radar-forwarded/plan.fixture.yaml +13 -0
  149. package/src/fixtures/data-radar-opaque/Scene.tsx +15 -0
  150. package/src/fixtures/data-radar-opaque/plan.fixture.yaml +10 -0
  151. package/src/fixtures/data-radar-tags/SceneClean.tsx +10 -0
  152. package/src/fixtures/data-radar-tags/SceneNullBinding.tsx +12 -0
  153. package/src/fixtures/data-radar-tags/SceneWithGap.tsx +11 -0
  154. package/src/fixtures/data-radar-tags/plan.fixture.yaml +29 -0
  155. package/src/fixtures/deterministic-scene.tsx +15 -0
  156. package/src/fixtures/double-shift/aliased.tsx +13 -0
  157. package/src/fixtures/double-shift/assigned.tsx +13 -0
  158. package/src/fixtures/double-shift/clean.tsx +19 -0
  159. package/src/fixtures/double-shift/destructured.tsx +12 -0
  160. package/src/fixtures/double-shift/elementAccess.tsx +12 -0
  161. package/src/fixtures/double-shift/layout.ts +24 -0
  162. package/src/fixtures/double-shift/mechanism-prefix.tsx.txt +458 -0
  163. package/src/fixtures/double-shift/wrapped.tsx +15 -0
  164. package/src/fixtures/geometry-sidecar-missing-export.ts +10 -0
  165. package/src/fixtures/geometry-sidecar-missing-type.ts +7 -0
  166. package/src/fixtures/geometry-sidecar-valid.ts +6 -0
  167. package/src/fixtures/interpolate-ascending.tsx +6 -0
  168. package/src/fixtures/interpolate-descending.tsx +8 -0
  169. package/src/fixtures/interpolate-identifier-keys.tsx +9 -0
  170. package/src/fixtures/nested-filters-broken.tsx +11 -0
  171. package/src/fixtures/nested-filters-fixed.tsx +8 -0
  172. package/src/fixtures/nested-filters-html-wrapper.tsx +14 -0
  173. package/src/fixtures/nested-filters-html.tsx +7 -0
  174. package/src/fixtures/nested-filters-siblings.tsx +12 -0
  175. package/src/fixtures/nondeterministic-scene.tsx +14 -0
  176. package/src/fixtures/nondeterministic-variants.tsx +16 -0
  177. package/src/fixtures/raw-hex-scene.tsx +10 -0
  178. package/src/fixtures/scene-root-declared/SceneHook.tsx +9 -0
  179. package/src/fixtures/scene-root-declared/plan.fixture.yaml +10 -0
  180. package/src/fixtures/scene-root-missing/SceneHook.tsx +11 -0
  181. package/src/fixtures/scene-root-missing/plan.fixture.yaml +10 -0
  182. package/src/fixtures/scene-root-no-names/SceneHook.tsx +6 -0
  183. package/src/fixtures/scene-root-no-names/plan.fixture.yaml +6 -0
  184. package/src/fixtures/scene-root-shell/SceneHook.tsx +8 -0
  185. package/src/fixtures/scene-root-shell/Shell.tsx +9 -0
  186. package/src/fixtures/scene-root-shell/plan.fixture.yaml +10 -0
  187. package/src/fixtures/shorthand-undefined-clean.tsx +23 -0
  188. package/src/fixtures/shorthand-undefined-longhand.tsx +17 -0
  189. package/src/fixtures/token-scene.tsx +9 -0
  190. package/src/includeCoverage.test.ts +176 -0
  191. package/src/learnedFrom.test.ts +32 -0
  192. package/src/projectDoc.test.ts +230 -0
  193. package/src/projectDoc.ts +152 -0
  194. package/src/projectDocSync.test.ts +152 -0
  195. package/src/projectDocSync.ts +79 -0
  196. package/src/projectMeta.test.ts +75 -0
  197. package/src/projectMeta.ts +53 -0
  198. package/src/provenance.ts +85 -0
  199. package/src/radar-check-ids.json +34 -0
  200. package/src/registry.test.ts +207 -0
  201. package/src/registry.ts +352 -0
  202. package/src/rules/animatedBarChartTightBox.ts +14 -0
  203. package/src/rules/animatedBarChartWhenNotToUse.ts +14 -0
  204. package/src/rules/animatedBarChartWhenToUse.ts +14 -0
  205. package/src/rules/animatedLineChartWhenNotToUse.ts +14 -0
  206. package/src/rules/animatedLineChartWhenToUse.ts +14 -0
  207. package/src/rules/approvedSfxPaletteOnly.ts +23 -0
  208. package/src/rules/atmosphereGradientsFadeAtBothEnds.ts +21 -0
  209. package/src/rules/audioIsTheClock.ts +22 -0
  210. package/src/rules/beatsDeriveFromTheirOwnSequence.ts +21 -0
  211. package/src/rules/bestSceneSetsTheBar.ts +21 -0
  212. package/src/rules/bumpBeforeRefreeze.ts +20 -0
  213. package/src/rules/cameraIsTheNarrator.ts +21 -0
  214. package/src/rules/checkPinBeforeShip.ts +22 -0
  215. package/src/rules/coldViewerClarity.ts +21 -0
  216. package/src/rules/commitOrCutEffects.ts +36 -0
  217. package/src/rules/convertFramesAcrossClockBoundary.ts +19 -0
  218. package/src/rules/crossCutElementsLiveInTheHudLayer.ts +21 -0
  219. package/src/rules/cursorClicksRealControls.ts +22 -0
  220. package/src/rules/cutOnRealAudioPauses.ts +20 -0
  221. package/src/rules/darkButNeverDim.ts +21 -0
  222. package/src/rules/dataRadarTagsDeclared.test.ts +60 -0
  223. package/src/rules/dataRadarTagsDeclared.ts +341 -0
  224. package/src/rules/declareAVerticalZoneMap.ts +19 -0
  225. package/src/rules/deleteRejectedRigsCompletely.ts +19 -0
  226. package/src/rules/deriveSeamsDontMeasure.ts +23 -0
  227. package/src/rules/deterministicSceneSource.test.ts +58 -0
  228. package/src/rules/deterministicSceneSource.ts +82 -0
  229. package/src/rules/diegeticSoundForOnscreenActions.ts +21 -0
  230. package/src/rules/distinctTransitionPerBeat.ts +21 -0
  231. package/src/rules/downloadOrDrawRichAssets.ts +22 -0
  232. package/src/rules/elementOnscreenForItsVoSentence.ts +25 -0
  233. package/src/rules/emitBuildNotes.ts +19 -0
  234. package/src/rules/energyFromTimingNotPartCount.ts +20 -0
  235. package/src/rules/evidenceBeforeClaimingFixed.ts +23 -0
  236. package/src/rules/exactPeerLockstep.ts +19 -0
  237. package/src/rules/firstPassIsADraft.ts +20 -0
  238. package/src/rules/fixCrowdingByRebandingNotNudging.ts +20 -0
  239. package/src/rules/fixesPreserveApprovedElements.ts +24 -0
  240. package/src/rules/floatingNotBoxed.ts +21 -0
  241. package/src/rules/focusBeatsRecedeTheContext.ts +20 -0
  242. package/src/rules/fontVarsBrandRemocn.ts +20 -0
  243. package/src/rules/geometricSeamsNotCrossfades.ts +28 -0
  244. package/src/rules/geometrySidecarExportsTyped.test.ts +37 -0
  245. package/src/rules/geometrySidecarExportsTyped.ts +59 -0
  246. package/src/rules/glowIsAFocalAccent.ts +28 -0
  247. package/src/rules/heroFillsTheFrame.ts +24 -0
  248. package/src/rules/htmlMasksForAnimatedCounters.ts +21 -0
  249. package/src/rules/impactStackOnPayoffs.ts +26 -0
  250. package/src/rules/interactiveTargetsAreDeclaredData.ts +21 -0
  251. package/src/rules/interpolateLiteralKeysMonotonic.test.ts +49 -0
  252. package/src/rules/interpolateLiteralKeysMonotonic.ts +98 -0
  253. package/src/rules/latenessIsStructuralOverlapTheBeats.ts +20 -0
  254. package/src/rules/lessTextMoreVisualizations.ts +22 -0
  255. package/src/rules/liveLinkDuplicateReactAlias.ts +22 -0
  256. package/src/rules/make2dElements3d.ts +21 -0
  257. package/src/rules/masterMixLoudnessTarget.ts +22 -0
  258. package/src/rules/measureAlignmentDontEyeball.ts +20 -0
  259. package/src/rules/motionCarriesWeightAndMeaning.ts +21 -0
  260. package/src/rules/motionDensityFloor.ts +20 -0
  261. package/src/rules/neverEditTierBVendored.ts +20 -0
  262. package/src/rules/neverRestateDerivedFrameNumbersInComments.ts +20 -0
  263. package/src/rules/noAiGradientLook.ts +22 -0
  264. package/src/rules/noColoredFlashToHideACut.ts +29 -0
  265. package/src/rules/noDoubleFrameShift.test.ts +95 -0
  266. package/src/rules/noDoubleFrameShift.ts +193 -0
  267. package/src/rules/noDuplicateMotionPrimitives.ts +19 -0
  268. package/src/rules/noLinearMotion.ts +29 -0
  269. package/src/rules/noNestedSvgFilters.test.ts +47 -0
  270. package/src/rules/noNestedSvgFilters.ts +138 -0
  271. package/src/rules/noRepeatedAssetAcrossShots.ts +19 -0
  272. package/src/rules/noShorthandWithUndefinedLonghand.test.ts +35 -0
  273. package/src/rules/noShorthandWithUndefinedLonghand.ts +140 -0
  274. package/src/rules/noStackedFadesAtACut.ts +30 -0
  275. package/src/rules/noStaticHolds.ts +24 -0
  276. package/src/rules/occlusionVisibleElements.ts +26 -0
  277. package/src/rules/oneContinuousVoTrack.ts +20 -0
  278. package/src/rules/onePunchPerMomentClass.ts +20 -0
  279. package/src/rules/oneRenderPerRound.ts +46 -0
  280. package/src/rules/openStaticAfterAHardCut.ts +29 -0
  281. package/src/rules/openingsAndClosingsCarryMore.ts +21 -0
  282. package/src/rules/persistentDeviceFrames.ts +20 -0
  283. package/src/rules/pixelDiffAnEmptyBandFirst.ts +20 -0
  284. package/src/rules/planIsPublishedNotGating.ts +32 -0
  285. package/src/rules/poweredByRevolaWatermark.ts +19 -0
  286. package/src/rules/predrawContentPresentAtTheCut.ts +21 -0
  287. package/src/rules/premiumSurfacesNotFlatBorders.ts +20 -0
  288. package/src/rules/radarDispositionsAreHumanOnly.ts +28 -0
  289. package/src/rules/realAssetsNotPlaceholders.ts +21 -0
  290. package/src/rules/rebuildUiFromReconNotScreenshots.ts +21 -0
  291. package/src/rules/reconStrandsRunInParallel.ts +63 -0
  292. package/src/rules/registryBeforeHandrolling.ts +20 -0
  293. package/src/rules/remapRateEndpointFormula.ts +19 -0
  294. package/src/rules/remocnContentTailwindChrome.ts +19 -0
  295. package/src/rules/retimeOnlyViaPlaybackOrDesignTokens.ts +18 -0
  296. package/src/rules/reviewTheRenderByFrameSweep.ts +23 -0
  297. package/src/rules/rollingNumberBoundedSequence.ts +21 -0
  298. package/src/rules/rollingNumberSequenceLayoutNone.ts +22 -0
  299. package/src/rules/rollingNumberTightBoxAlignment.ts +19 -0
  300. package/src/rules/rollingNumberWhenNotToUse.ts +24 -0
  301. package/src/rules/rollingNumberWhenToUse.ts +23 -0
  302. package/src/rules/sameFamilySameProject.ts +20 -0
  303. package/src/rules/sampleBrandFromProduct.ts +19 -0
  304. package/src/rules/sampleVelocityFromPreviousFramePosition.ts +21 -0
  305. package/src/rules/sceneRootTagDeclared.test.ts +43 -0
  306. package/src/rules/sceneRootTagDeclared.ts +78 -0
  307. package/src/rules/scrubDemoDataToGenericRoles.ts +19 -0
  308. package/src/rules/selfReviewThePlanForGaps.ts +47 -0
  309. package/src/rules/sourceArchivedBeforeBrief.ts +24 -0
  310. package/src/rules/statisticsSelfExplanatory.ts +18 -0
  311. package/src/rules/stopsAsRelativeSpacing.ts +21 -0
  312. package/src/rules/theWholeCanvasWorks.ts +21 -0
  313. package/src/rules/themePerSection.ts +28 -0
  314. package/src/rules/thesisDeclaredInBrief.ts +25 -0
  315. package/src/rules/tierARequiresPropUnreachableEdit.ts +20 -0
  316. package/src/rules/tinyBodyCopyAsRuledLines.ts +19 -0
  317. package/src/rules/titlesPinnedOutsideCameraPush.ts +22 -0
  318. package/src/rules/tokensNotRawHexes.test.ts +51 -0
  319. package/src/rules/tokensNotRawHexes.ts +89 -0
  320. package/src/rules/typeTokenDefaultedParamsAsString.ts +21 -0
  321. package/src/rules/varyKitChoreographyAcrossSections.ts +21 -0
  322. package/src/rules/varyTheVisualizationGrammar.ts +22 -0
  323. package/src/rules/verifyCheapRenderDear.ts +18 -0
  324. package/src/rules/voAddressesTheViewer.ts +18 -0
  325. package/src/rules/voIsAPerformance.ts +21 -0
  326. package/src/rules/voScriptLocksSpokenWords.ts +21 -0
  327. package/src/rules/wrappersPreserveChildGeometry.ts +22 -0
  328. package/src/sourceCheck.test.ts +50 -0
  329. package/src/sourceCheck.ts +55 -0
  330. package/src/sourceCitations.test.ts +747 -0
  331. package/src/sourceCitations.ts +217 -0
  332. package/src/stageProjection.ts +112 -0
  333. package/src/stages.test.ts +68 -0
  334. package/src/stages.ts +207 -0
  335. package/src/testSupport.ts +16 -0
  336. package/src/types.ts +181 -0
  337. package/src/variants.test.ts +24 -0
  338. package/src/variants.ts +13 -0
  339. package/src/waiverProjection.test.ts +59 -0
  340. package/src/waiverProjection.ts +45 -0
@@ -0,0 +1,15 @@
1
+ const darkButNeverDim = {
2
+ id: "dark-but-never-dim",
3
+ claim: "The dark cinematic canvas never costs legibility: lift values, use lighter material (white-paper artifacts on the dark shell), keep tonal variety inside the theme, and hold the canvas consistent scene to scene - 'too dark to read comfortably' and 'these scenes look like different videos' are both hard fails.",
4
+ why: "'Very low contrast, it exhausted my eyes' and 'add more colors, everything looks too dark, but don't go outside the theming' show the dark theme failing on comfort while staying on brand; darkness is the style, dimness and canvas drift are the defects.",
5
+ learnedFrom: "docs/style/CREATIVE_EDGE.md",
6
+ tier: "craft",
7
+ scope: { kind: "pipeline" },
8
+ enforcement: {
9
+ kind: "prose",
10
+ unanchorable: "A contrast-and-consistency judgment across scenes at 1080p; no single code location expresses it."
11
+ }
12
+ };
13
+ export {
14
+ darkButNeverDim
15
+ };
@@ -0,0 +1,193 @@
1
+ import { existsSync, readFileSync } from "node:fs";
2
+ import { dirname, join, relative, sep } from "node:path";
3
+ import { globSync } from "tinyglobby";
4
+ import { SyntaxKind } from "ts-morph";
5
+ import { parse as parseYaml } from "yaml";
6
+ import { matchedFiles } from "../sourceCheck.js";
7
+ const toPosix = (p) => p.split(sep).join("/");
8
+ const INCLUDE = [
9
+ "qpa-video/03_scenes/**/*.{js,jsx,ts,tsx}",
10
+ "revola-video/src/scenes/**/*.{js,jsx,ts,tsx}",
11
+ "workday-dple-video/src/scenes/**/*.{js,jsx,ts,tsx}",
12
+ "datapeak-video/**/*.{js,jsx,ts,tsx}",
13
+ "datapeak-usecases/**/*.{js,jsx,ts,tsx}",
14
+ "qse-video/**/*.{js,jsx,ts,tsx}",
15
+ "qse-investor-video/**/*.{js,jsx,ts,tsx}",
16
+ "qse-qguard-video/src/**/*.{ts,tsx}",
17
+ "spec-videos/**/*.{js,jsx,ts,tsx}",
18
+ "klaviyo-segments-video/**/*.{js,jsx,ts,tsx}",
19
+ "radar-fixture-video/src/**/*.{js,jsx,ts,tsx}",
20
+ "rcp-fund7-video/src/**/*.{ts,tsx}",
21
+ "expanse-video/src/**/*.{ts,tsx}",
22
+ "reverence-video/src/**/*.{ts,tsx}"
23
+ ];
24
+ function findPlanDir(startDir, root) {
25
+ let dir = startDir;
26
+ for (; ; ) {
27
+ if (globSync("plan*.yaml", { cwd: dir }).length > 0) return dir;
28
+ if (dir === root || !dir.startsWith(root)) return null;
29
+ const parent = dirname(dir);
30
+ if (parent === dir) return null;
31
+ dir = parent;
32
+ }
33
+ }
34
+ function declaredElements(planDir, root) {
35
+ const out = [];
36
+ for (const planFile of globSync("plan*.yaml", { cwd: planDir, absolute: true })) {
37
+ let plan;
38
+ try {
39
+ plan = parseYaml(readFileSync(planFile, "utf8"));
40
+ } catch {
41
+ continue;
42
+ }
43
+ for (const section of plan.sections ?? []) {
44
+ for (const binding of section.names ?? []) {
45
+ if (typeof binding.element !== "string") continue;
46
+ out.push({
47
+ sectionId: section.id ?? "(unnamed section)",
48
+ elementId: binding.element,
49
+ planFile: toPosix(relative(root, planFile))
50
+ });
51
+ }
52
+ }
53
+ }
54
+ return out;
55
+ }
56
+ const isTagAttrName = (name) => name === "data-radar";
57
+ const attrLiteralsByName = (files) => {
58
+ const byName = /* @__PURE__ */ new Map();
59
+ for (const file of files) {
60
+ for (const attr of file.getDescendantsOfKind(SyntaxKind.JsxAttribute)) {
61
+ const value = literalOf(attr.getInitializer());
62
+ if (value === null) continue;
63
+ const name = attr.getNameNode().getText();
64
+ const set = byName.get(name) ?? /* @__PURE__ */ new Set();
65
+ set.add(value);
66
+ byName.set(name, set);
67
+ }
68
+ }
69
+ return byName;
70
+ };
71
+ function literalOf(init) {
72
+ if (!init) return null;
73
+ if (init.isKind(SyntaxKind.StringLiteral)) return init.getLiteralValue();
74
+ if (init.isKind(SyntaxKind.JsxExpression)) {
75
+ const expr = init.getExpression();
76
+ if (!expr) return null;
77
+ if (expr.isKind(SyntaxKind.StringLiteral)) return expr.getLiteralValue();
78
+ if (expr.isKind(SyntaxKind.NoSubstitutionTemplateLiteral)) return expr.getLiteralValue();
79
+ }
80
+ return null;
81
+ }
82
+ function templatePattern(text) {
83
+ const body = text.replace(/^`|`$/g, "");
84
+ if (!body.includes("${")) return null;
85
+ const escaped = body.split(/\$\{[^}]*\}/g).map((chunk) => chunk.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join(".+");
86
+ return new RegExp(`^${escaped}$`);
87
+ }
88
+ function buildTagIndex(files, root) {
89
+ const literals = /* @__PURE__ */ new Set();
90
+ const patterns = [];
91
+ const opaque = [];
92
+ const byAttrName = attrLiteralsByName(files);
93
+ for (const file of files) {
94
+ const rel = toPosix(relative(root, file.getFilePath()));
95
+ for (const attr of file.getDescendantsOfKind(SyntaxKind.JsxAttribute)) {
96
+ if (!isTagAttrName(attr.getNameNode().getText())) continue;
97
+ const init = attr.getInitializer();
98
+ const literal = literalOf(init);
99
+ if (literal !== null) {
100
+ literals.add(literal);
101
+ continue;
102
+ }
103
+ const expr = init?.isKind(SyntaxKind.JsxExpression) ? init.getExpression() : void 0;
104
+ if (expr?.isKind(SyntaxKind.TemplateExpression)) {
105
+ const pattern = templatePattern(expr.getText());
106
+ if (pattern) {
107
+ patterns.push(pattern);
108
+ continue;
109
+ }
110
+ }
111
+ if (expr?.isKind(SyntaxKind.Identifier)) {
112
+ const forwarded = byAttrName.get(expr.getText());
113
+ if (forwarded?.size) {
114
+ for (const v of forwarded) literals.add(v);
115
+ continue;
116
+ }
117
+ }
118
+ opaque.push({ file: rel, line: attr.getStartLineNumber(), text: attr.getText().slice(0, 60) });
119
+ }
120
+ }
121
+ return { literals, patterns, opaque };
122
+ }
123
+ const IGNORES = ["**/node_modules/**", "**/.worktrees/**", "**/04_exports/**", "**/*.test.{ts,tsx,jsx}", "**/build/**"];
124
+ const includedUnder = (planDir, check, root) => matchedFiles(check, root).filter((f) => f.startsWith(planDir + sep));
125
+ function projectSourceFiles(planDir, check, root) {
126
+ const included = includedUnder(planDir, check, root);
127
+ if (included.length) return included;
128
+ return globSync("**/*.{js,jsx,ts,tsx}", { cwd: planDir, absolute: true, ignore: IGNORES }).sort();
129
+ }
130
+ function isReporterFile(planDir, absPath, check, root) {
131
+ const included = includedUnder(planDir, check, root);
132
+ return included.length === 0 || included[0] === absPath;
133
+ }
134
+ const dataRadarTagsDeclared = {
135
+ id: "data-radar-tags-declared",
136
+ claim: "Every element id a plan's `names:` binding declares carries a matching `data-radar` attribute somewhere in that project's source.",
137
+ why: "vo-element-sync joins a declared binding to speech-map timings and geometry samples; a declared element-id with no matching data-radar tag anywhere in the project is a certain finding, and catching it at authoring time (no render, no sampler) is strictly cheaper than catching it at radar:scene time.",
138
+ learnedFrom: "docs/superpowers/specs/2026-07-31-defect-radar-design.md",
139
+ tier: "gate",
140
+ verify: "`vitest run packages/video-craft` (source-level, no render) - `video radar:scene --project <dir> --comp <id> --check tag-coverage` re-verifies the same binding against sampled frames at runtime",
141
+ owner: "tag-coverage",
142
+ scope: { kind: "pipeline" },
143
+ enforcement: {
144
+ kind: "source",
145
+ check: {
146
+ include: INCLUDE,
147
+ run(file, relPath, root) {
148
+ const absPath = join(root, relPath);
149
+ const planDir = findPlanDir(dirname(absPath), root);
150
+ if (!planDir || !existsSync(planDir)) return [];
151
+ if (dataRadarTagsDeclared.enforcement.kind !== "source") return [];
152
+ const selfCheck = dataRadarTagsDeclared.enforcement.check;
153
+ if (!isReporterFile(planDir, absPath, selfCheck, root)) return [];
154
+ const declared = declaredElements(planDir, root);
155
+ if (declared.length === 0) return [];
156
+ const project = file.getProject();
157
+ const index = buildTagIndex(
158
+ projectSourceFiles(planDir, selfCheck, root).map((f) => project.addSourceFileAtPath(f)),
159
+ root
160
+ );
161
+ const findings = [];
162
+ for (const site of index.opaque) {
163
+ findings.push({
164
+ file: site.file,
165
+ line: site.line,
166
+ match: site.text,
167
+ message: `data-radar is bound to an expression this gate cannot evaluate (${site.text}), so no declared element id in ${toPosix(relative(root, planDir))} can be verified from source. Use a string literal, a template whose literal prefix is stable, or forward it through a prop named \`dataRadar\` that call sites pass a literal to.`
168
+ });
169
+ }
170
+ if (findings.length) return findings;
171
+ for (const { sectionId, elementId, planFile } of declared) {
172
+ if (index.literals.has(elementId)) continue;
173
+ if (index.patterns.some((p) => p.test(elementId))) continue;
174
+ findings.push({
175
+ file: planFile,
176
+ line: 1,
177
+ match: elementId,
178
+ message: `plan section "${sectionId}" declares names: element "${elementId}", but no data-radar="${elementId}" attribute exists anywhere in ${toPosix(relative(root, planDir))}'s source. Tag the element the cue names, or make the binding an explained \`element: null\`.`
179
+ });
180
+ }
181
+ return findings;
182
+ }
183
+ }
184
+ }
185
+ };
186
+ export {
187
+ INCLUDE,
188
+ dataRadarTagsDeclared,
189
+ declaredElements,
190
+ findPlanDir,
191
+ isReporterFile,
192
+ projectSourceFiles
193
+ };
@@ -0,0 +1,15 @@
1
+ const declareAVerticalZoneMap = {
2
+ id: "declare-a-vertical-zone-map",
3
+ claim: "Declare a vertical zone map at the top of each scene file - copy band, breath band (nothing may enter), stage band - and make camera keys and world layout enforce that animation never rises into the copy band.",
4
+ why: "Without declared bands, text and animation collide (a tag overlapping the headline, fog slicing the clock) and each collision gets patched ad hoc.",
5
+ learnedFrom: "revola-video/BUILD_NOTES_CRAFT.md",
6
+ tier: "craft",
7
+ scope: { kind: "pipeline" },
8
+ enforcement: {
9
+ kind: "prose",
10
+ anchor: { file: "revola-video/src/scenes/craft/S3Line.tsx" }
11
+ }
12
+ };
13
+ export {
14
+ declareAVerticalZoneMap
15
+ };
@@ -0,0 +1,15 @@
1
+ const deleteRejectedRigsCompletely = {
2
+ id: "delete-rejected-rigs-completely",
3
+ claim: "When a transition or effect is rejected in review, delete the rig, its tokens, and its plumbing (props, overlay slots) entirely; revert call sites rather than leaving the machinery dead.",
4
+ why: "The rejected light bridge left LightBridge, BRIDGE_* tokens, and the CraftStage overlay prop as attractive dead plumbing; deleting it and reverting S2's usage is what makes the rejection stick.",
5
+ learnedFrom: "revola-video/BUILD_NOTES_CRAFT.md",
6
+ tier: "process",
7
+ scope: { kind: "pipeline" },
8
+ enforcement: {
9
+ kind: "prose",
10
+ unanchorable: "The artifact of following this rule is the absence of code; there is no symbol to point at."
11
+ }
12
+ };
13
+ export {
14
+ deleteRejectedRigsCompletely
15
+ };
@@ -0,0 +1,17 @@
1
+ const deriveSeamsDontMeasure = {
2
+ id: "derive-seams-dont-measure",
3
+ claim: "Derive a cross-scene seam from the neighbouring scene's exported geometry, never from a number measured off a render.",
4
+ why: "A measured constant is a bug with a delay fuse: it stays correct until the neighbouring scene is recut, then silently misplaces the seam with nothing failing. Deriving it means a recut re-derives the seam for free.",
5
+ learnedFrom: "revola-video/BUILD_NOTES_CRAFT.md",
6
+ tier: "gate",
7
+ verify: "`video radar:film --project <dir> --video <mp4> --comp <id> --check seam-fidelity`",
8
+ owner: "seam-fidelity",
9
+ scope: { kind: "pipeline" },
10
+ enforcement: {
11
+ kind: "prose",
12
+ anchor: { file: "revola-video/src/scenes/craft/S1Graveyard.tsx", symbol: "STRIKE_PTS" }
13
+ }
14
+ };
15
+ export {
16
+ deriveSeamsDontMeasure
17
+ };
@@ -0,0 +1,65 @@
1
+ import { SyntaxKind } from "ts-morph";
2
+ const deterministicSceneSource = {
3
+ id: "deterministic-scene-source",
4
+ claim: "Scene and kit source must be render-deterministic: no Math.random(), no Date.now(), no argless new Date(); use seeded randomness and explicit dates.",
5
+ why: "A nondeterministic value is re-evaluated on every render pass, so elements jitter between frames and two renders of the same composition differ; the sessions caught these only via an ad-hoc grep gate after the fact.",
6
+ learnedFrom: "claude-sessions-digest/revola-usecase-book-meeting.md",
7
+ upstream: "remotion:@remotion/eslint-plugin/deterministic-randomness",
8
+ tier: "gate",
9
+ verify: "render the same frame range twice and diff every frame; all must report YMAX=0 (automation owed: remotion-videos-5oi)",
10
+ scope: { kind: "pipeline" },
11
+ enforcement: {
12
+ kind: "source",
13
+ check: {
14
+ // Determinism is a crash-class invariant, not a per-project doctrine,
15
+ // so the blast radius matches interpolate-literal-keys-monotonic: every
16
+ // scene tree plus the kit. The corpus runs clean across all of them.
17
+ include: [
18
+ "qpa-video/03_scenes/**/*.{js,jsx,ts,tsx}",
19
+ "reverence-video/src/scenes/**/*.{js,jsx,ts,tsx}",
20
+ "workday-dple-video/src/scenes/**/*.{js,jsx,ts,tsx}",
21
+ "revola-video/src/scenes/**/*.{js,jsx,ts,tsx}",
22
+ "datapeak-video/**/*.{js,jsx,ts,tsx}",
23
+ "datapeak-usecases/**/*.{js,jsx,ts,tsx}",
24
+ "qse-video/**/*.{js,jsx,ts,tsx}",
25
+ "qse-investor-video/**/*.{js,jsx,ts,tsx}",
26
+ "qse-qguard-video/src/**/*.{ts,tsx}",
27
+ "rcp-fund7-video/src/**/*.{ts,tsx}",
28
+ "expanse-video/src/**/*.{ts,tsx}",
29
+ "reverence-video/src/**/*.{ts,tsx}",
30
+ "spec-videos/**/*.{js,jsx,ts,tsx}",
31
+ // Added Task 23: klaviyo-segments-video and radar-fixture-video were
32
+ // silently excluded (this list said "every scene tree" but named
33
+ // neither) - the exact silent-default trap this rule's own claim
34
+ // exists to catch, applied to the checker itself.
35
+ "klaviyo-segments-video/src/**/*.{js,jsx,ts,tsx}",
36
+ "radar-fixture-video/src/**/*.{js,jsx,ts,tsx}",
37
+ "packages/video-kit/src/**/*.{ts,tsx}"
38
+ ],
39
+ run(file, relPath) {
40
+ const findings = [];
41
+ const report = (node, what) => {
42
+ findings.push({
43
+ file: relPath,
44
+ line: node.getStartLineNumber(),
45
+ match: node.getText().slice(0, 60),
46
+ message: `nondeterministic ${what} in scene/kit source; seed it or pass an explicit value`
47
+ });
48
+ };
49
+ for (const call of file.getDescendantsOfKind(SyntaxKind.CallExpression)) {
50
+ const callee = call.getExpression().getText();
51
+ if (callee === "Math.random" || callee === "Date.now") report(call, `${callee}()`);
52
+ }
53
+ for (const nw of file.getDescendantsOfKind(SyntaxKind.NewExpression)) {
54
+ if (nw.getExpression().getText() === "Date" && nw.getArguments().length === 0) {
55
+ report(nw, "new Date()");
56
+ }
57
+ }
58
+ return findings;
59
+ }
60
+ }
61
+ }
62
+ };
63
+ export {
64
+ deterministicSceneSource
65
+ };
@@ -0,0 +1,15 @@
1
+ const diegeticSoundForOnscreenActions = {
2
+ id: "diegetic-sound-for-onscreen-actions",
3
+ claim: "On-screen actions are heard: typing on screen plays asmr-typing.wav in sync, clicks land a soft UI click on the click frame, seams never cross in dead silence, and the music bed is an upbeat tech-reveal track (the Linear-reel EDM track is the reference), subtle and ducked under payoffs.",
4
+ why: "'Use asmr-typing.wav anywhere that shows typing' canonized the diegetic-sync expectation, and 'use a more upbeat bg, maybe the one used in the Linear reel' pinned the bed genre; the palette rule alone let silent typing and a wrong-genre bed through.",
5
+ learnedFrom: "docs/style/CREATIVE_EDGE.md",
6
+ tier: "craft",
7
+ scope: { kind: "pipeline" },
8
+ enforcement: {
9
+ kind: "prose",
10
+ unanchorable: "An audio-to-visual sync judgment against the render; no single code location expresses it."
11
+ }
12
+ };
13
+ export {
14
+ diegeticSoundForOnscreenActions
15
+ };
@@ -0,0 +1,15 @@
1
+ const distinctTransitionPerBeat = {
2
+ id: "distinct-transition-per-beat",
3
+ claim: "Adjacent beats use distinct, directionally coherent transitions (exit left pairs with enter right, exit up with enter down); never the same transition twice in a row, never an unmotivated hard swap.",
4
+ why: "Back-to-back identical transitions read as template output, and directionally incoherent exits/entries were flagged as 'half left, half middle' confusion; direction logic is what makes cuts feel authored.",
5
+ learnedFrom: "claude-sessions-digest/revola-usecase-book-meeting.md",
6
+ tier: "craft",
7
+ scope: { kind: "pipeline" },
8
+ enforcement: {
9
+ kind: "prose",
10
+ unanchorable: "The rule is a sequencing judgment across scene boundaries; no single code location expresses it."
11
+ }
12
+ };
13
+ export {
14
+ distinctTransitionPerBeat
15
+ };
@@ -0,0 +1,15 @@
1
+ const downloadOrDrawRichAssets = {
2
+ id: "download-or-draw-rich-assets",
3
+ claim: "When a beat needs an element, download the real asset (brand SVG, icon set, map or geo data, texture, 3D model, font) at authoring time and commit it, or hand-author a detailed custom SVG; a styled-div rectangle, basic shape, or emoji standing in for art is a defect, and render-time fetching is banned so renders stay deterministic.",
4
+ why: "The gap between prompted builds that looked professional and pipeline builds that looked flat was largely asset quality: agents defaulted to CSS primitives unless told downloading was allowed, while the standing instruction 'download anything you want, or make custom SVGs' produced rich marks every time it was issued.",
5
+ learnedFrom: "docs/style/CREATIVE_EDGE.md",
6
+ tier: "craft",
7
+ scope: { kind: "pipeline" },
8
+ enforcement: {
9
+ kind: "prose",
10
+ unanchorable: "An asset-quality judgment per visible element; no single code location expresses it."
11
+ }
12
+ };
13
+ export {
14
+ downloadOrDrawRichAssets
15
+ };
@@ -0,0 +1,17 @@
1
+ const elementOnscreenForItsVoSentence = {
2
+ id: "element-onscreen-for-its-vo-sentence",
3
+ claim: "The element a voiceover sentence names is fully on screen, build-in complete, for the whole duration of that sentence; secondary marks (partner logos) enter when mentioned, not at frame zero.",
4
+ why: "Visuals lagging or leading their VO line was the dominant ACT854 feedback category, re-broken by every new voiceover take; the sentence-to-element contract is what re-timing must preserve.",
5
+ learnedFrom: "claude-sessions-digest/act854-qpa-video.md",
6
+ tier: "gate",
7
+ verify: "`video radar:scene --project <dir> --comp <id> --check vo-element-sync --check edge-touch`",
8
+ owner: "vo-element-sync",
9
+ scope: { kind: "pipeline" },
10
+ enforcement: {
11
+ kind: "prose",
12
+ anchor: { file: "packages/video-cli/src/measure.ts" }
13
+ }
14
+ };
15
+ export {
16
+ elementOnscreenForItsVoSentence
17
+ };
@@ -0,0 +1,15 @@
1
+ const emitBuildNotes = {
2
+ id: "emit-build-notes",
3
+ claim: "Maintain BUILD_NOTES.md (or the automated equivalent) per video: current wired WAV, frame layout, and every feedback decision, newest round on top.",
4
+ why: "Without it nobody, human or agent, can pick the project up later; the notes are the only record of why timings and content are what they are.",
5
+ learnedFrom: "HOW_I_BUILD_VIDEOS.md",
6
+ tier: "process",
7
+ scope: { kind: "pipeline" },
8
+ enforcement: {
9
+ kind: "prose",
10
+ unanchorable: "A documentation convention; its exemplars are docs, not live code."
11
+ }
12
+ };
13
+ export {
14
+ emitBuildNotes
15
+ };
@@ -0,0 +1,15 @@
1
+ const energyFromTimingNotPartCount = {
2
+ id: "energy-from-timing-not-part-count",
3
+ claim: "Scene energy comes from timing and material, never from part count: one subject performing beautifully beats ten things wiggling, and a beat where several subjects compete for the eye at once is as much a defect as a static hold.",
4
+ why: "'It looks overwhelming, I wanted to do it more with the camera' arrived in the same corpus as the too-static notes; answering flatness by adding elements produced the opposite failure, so density must be bought with overlapping beats in time, not with simultaneous objects.",
5
+ learnedFrom: "docs/style/CREATIVE_EDGE.md",
6
+ tier: "craft",
7
+ scope: { kind: "pipeline" },
8
+ enforcement: {
9
+ kind: "prose",
10
+ unanchorable: "An attention-competition judgment per beat; no single code location expresses it."
11
+ }
12
+ };
13
+ export {
14
+ energyFromTimingNotPartCount
15
+ };
@@ -0,0 +1,15 @@
1
+ const evidenceBeforeClaimingFixed = {
2
+ id: "evidence-before-claiming-fixed",
3
+ claim: "Never report a fix without evidence from the artifact itself: a still at the affected frames, a grep proving the removed effect is gone, or an audio probe proving the sound is present.",
4
+ why: "Three separate 'fixes' shipped unverified and bounced with 'it's still there'; each unverified claim costs a full human review round, the most expensive resource in the loop.",
5
+ learnedFrom: "claude-sessions-digest/portfolio-reels.md",
6
+ tier: "process",
7
+ scope: { kind: "pipeline" },
8
+ enforcement: {
9
+ kind: "prose",
10
+ unanchorable: "The rule prescribes review-loop discipline; no repo code implements evidence capture to point at."
11
+ }
12
+ };
13
+ export {
14
+ evidenceBeforeClaimingFixed
15
+ };
@@ -0,0 +1,15 @@
1
+ const exactPeerLockstep = {
2
+ id: "exact-peer-lockstep",
3
+ claim: "The kit pins its peers exactly; a project that bumps Remotion must get a matching kit release before its next freeze.",
4
+ why: "A peer-range mismatch lets a project silently render against a Remotion version the kit was never tested with, or blocks the freeze at the worst moment.",
5
+ learnedFrom: "HOW_I_BUILD_VIDEOS.md",
6
+ tier: "process",
7
+ scope: { kind: "pipeline" },
8
+ enforcement: {
9
+ kind: "prose",
10
+ anchor: { file: "packages/video-cli/src/kitShell.ts" }
11
+ }
12
+ };
13
+ export {
14
+ exactPeerLockstep
15
+ };
@@ -0,0 +1,15 @@
1
+ const firstPassIsADraft = {
2
+ id: "first-pass-is-a-draft",
3
+ claim: "Treat every scene's first build as a draft: a dedicated amplification pass (faster, more detailed, more layered motion; unnecessary text removed) is a mandatory pipeline stage, not an optional polish.",
4
+ why: "In the best-documented build, all five scenes received the same 'too basic, blow people away' round; budgeting the second pass up front converts a guaranteed feedback round into a pipeline stage.",
5
+ learnedFrom: "claude-sessions-digest/revola-craft-video.md",
6
+ tier: "process",
7
+ scope: { kind: "pipeline" },
8
+ enforcement: {
9
+ kind: "prose",
10
+ unanchorable: "The rule prescribes a pipeline stage; no code location expresses it yet."
11
+ }
12
+ };
13
+ export {
14
+ firstPassIsADraft
15
+ };
@@ -0,0 +1,15 @@
1
+ const fixCrowdingByRebandingNotNudging = {
2
+ id: "fix-crowding-by-rebanding-not-nudging",
3
+ claim: "When a layout reads crowded or crumbled, fix the band allocation by moving the whole stage, not by nudging individual elements.",
4
+ why: "The S3 spine at y660 squeezed every station into 360px while the bottom 240px of frame sat empty; the root cause was layout allocation, not element size, and shrinking elements would not have fixed it.",
5
+ learnedFrom: "revola-video/BUILD_NOTES_CRAFT.md",
6
+ tier: "craft",
7
+ scope: { kind: "pipeline" },
8
+ enforcement: {
9
+ kind: "prose",
10
+ anchor: { file: "revola-video/src/scenes/craft/S3Line.tsx" }
11
+ }
12
+ };
13
+ export {
14
+ fixCrowdingByRebandingNotNudging
15
+ };
@@ -0,0 +1,15 @@
1
+ const fixesPreserveApprovedElements = {
2
+ id: "fixes-preserve-approved-elements",
3
+ claim: "A fix scoped to one complaint is a minimal diff against the approved baseline: additive unless removal was requested, and previously-praised elements are never deleted while fixing a neighbour.",
4
+ why: "Twice a scoped complaint was 'fixed' by deleting elements the reviewer loved, costing a restoration round each time; feedback on one aspect of a scene is not license to rebuild the scene.",
5
+ learnedFrom: "claude-sessions-digest/revola-usecase-book-meeting.md",
6
+ tier: "process",
7
+ scope: { kind: "pipeline" },
8
+ enforcement: {
9
+ kind: "prose",
10
+ unanchorable: "The rule prescribes diff discipline for feedback rounds; no code location expresses it."
11
+ }
12
+ };
13
+ export {
14
+ fixesPreserveApprovedElements
15
+ };
@@ -0,0 +1,15 @@
1
+ const floatingNotBoxed = {
2
+ id: "floating-not-boxed",
3
+ claim: "Content floats free on the canvas, grounded by soft contact shadows, instead of sitting in boxed panels; every container must earn its chrome - a filler background rectangle behind an SVG is a defect, and when a panel does exist it is a premium layered surface.",
4
+ why: "'I prefer floating svgs instead of containers' and 'remove the ugly bg colors from the SVGs - let them float' were standing preferences across rounds; boxed content read as template while floating, shadow-grounded elements read as designed.",
5
+ learnedFrom: "docs/style/CREATIVE_EDGE.md",
6
+ tier: "craft",
7
+ scope: { kind: "pipeline" },
8
+ enforcement: {
9
+ kind: "prose",
10
+ unanchorable: "A does-this-container-earn-its-chrome judgment per element; no single code location expresses it."
11
+ }
12
+ };
13
+ export {
14
+ floatingNotBoxed
15
+ };
@@ -0,0 +1,15 @@
1
+ const focusBeatsRecedeTheContext = {
2
+ id: "focus-beats-recede-the-context",
3
+ claim: "When a beat introduces a new subject over existing UI, recede the context behind it - dim, blur, scale back, scrim - and give the new subject cleared space; never draw the new layer on top of live content.",
4
+ why: "A diagram drawn over a fully lit room put its label character-for-character on top of a card's caption and its connector lines read as scratches across the UI; receding the room turned the same beat into a clean shot with no geometry changes.",
5
+ learnedFrom: "claude-sessions-digest/product-room-video.md",
6
+ tier: "craft",
7
+ scope: { kind: "pipeline" },
8
+ enforcement: {
9
+ kind: "prose",
10
+ unanchorable: "A composition discipline spanning scene and overlay layers; no single symbol owns it."
11
+ }
12
+ };
13
+ export {
14
+ focusBeatsRecedeTheContext
15
+ };
@@ -0,0 +1,15 @@
1
+ const fontVarsBrandRemocn = {
2
+ id: "font-vars-brand-remocn",
3
+ claim: "Brand all remocn components at once by pointing --font-geist-sans and --font-geist-mono at the project's loaded fonts in CSS; never touch font settings in component source.",
4
+ why: "Editing fonts per-component forfeits Tier B update-safety and reintroduces per-file drift; the CSS vars brand ~110 components with zero source changes.",
5
+ learnedFrom: "HOW_I_BUILD_VIDEOS.md",
6
+ tier: "craft",
7
+ scope: { kind: "pipeline" },
8
+ enforcement: {
9
+ kind: "prose",
10
+ anchor: { file: "revola-video/src/index.css" }
11
+ }
12
+ };
13
+ export {
14
+ fontVarsBrandRemocn
15
+ };
@@ -0,0 +1,23 @@
1
+ const geometricSeamsNotCrossfades = {
2
+ id: "geometric-seams-not-crossfades",
3
+ claim: "Make a seam physical: the clicked element becomes the next scene, or the camera travels through it; overlap adjacent sections by a few frames so the handoff happens mid-air rather than as a dissolve between slides.",
4
+ why: "Praised cuts in the reference films are all motivated - a card expands into the next shot, the camera flies through a control - while crossfades read as slides advancing; the geometric handoff is also what makes a cut survive a re-time.",
5
+ learnedFrom: "claude-sessions-digest/product-room-video.md",
6
+ tier: "gate",
7
+ // Added by the codex red team round (2026-08-01): the r2 sudden-seams class
8
+ // (5 of 6 seams, BUILD_NOTES round 2 feedback item 1, bead
9
+ // remotion-videos-t0s) escaped every prior check because the two seam bans
10
+ // this rule states are satisfiable by doing nothing - `seam-continuity` is
11
+ // the check that verifies the CONSTRUCTIVE half: every seam carries either
12
+ // a shared geometry-sidecar carrier or a declared overlap of >=4 frames.
13
+ verify: "`video radar:film --project <dir> --video <mp4> --check seam-continuity`",
14
+ owner: "seam-continuity",
15
+ scope: { kind: "pipeline" },
16
+ enforcement: {
17
+ kind: "prose",
18
+ anchor: { file: "revola-video/src/productroom/PRColdOpen.tsx" }
19
+ }
20
+ };
21
+ export {
22
+ geometricSeamsNotCrossfades
23
+ };
@@ -0,0 +1,36 @@
1
+ const geometrySidecarExportsTyped = {
2
+ id: "geometry-sidecar-exports-typed",
3
+ claim: "A scene's geometry sidecar exports `const geometry` typed as `SceneGeometry`.",
4
+ why: "The compose:registry generator and film assembly import a seam's geometry sidecar by convention (src/scenes/scene.<id>.geometry.ts) and read its `geometry` export at compile time; an untyped or missing export turns that compile-time check into a silent `undefined` at runtime, at the exact moment - a scene cut - where the seam contract is supposed to be enforced.",
5
+ learnedFrom: "docs/superpowers/plans/2026-07-30-video-task-graph-phase1.md",
6
+ tier: "gate",
7
+ verify: "`vitest run packages/video-craft` - machine-checked over every geometry sidecar file",
8
+ scope: { kind: "pipeline" },
9
+ enforcement: {
10
+ kind: "source",
11
+ check: {
12
+ // One level deep under each project directory, matching
13
+ // geometrySidecarPath()'s convention exactly: <project>/src/scenes/scene.<id>.geometry.ts.
14
+ include: ["*/src/scenes/scene.*.geometry.ts"],
15
+ run(file, relPath) {
16
+ const text = file.getFullText();
17
+ const hasExport = text.includes("export const geometry");
18
+ const hasType = text.includes(": SceneGeometry");
19
+ if (hasExport && hasType) return [];
20
+ const missing = [
21
+ !hasExport && "`export const geometry`",
22
+ !hasType && "`: SceneGeometry`"
23
+ ].filter((x) => !!x).join(" and ");
24
+ return [{
25
+ file: relPath,
26
+ line: 1,
27
+ match: relPath.split("/").pop() ?? relPath,
28
+ message: `geometry sidecar is missing ${missing}; a sidecar at this path must read \`export const geometry: SceneGeometry = { ... }\` (packages/video-kit/src/pure/geometry.ts)`
29
+ }];
30
+ }
31
+ }
32
+ }
33
+ };
34
+ export {
35
+ geometrySidecarExportsTyped
36
+ };
@@ -0,0 +1,22 @@
1
+ const glowIsAFocalAccent = {
2
+ id: "glow-is-a-focal-accent",
3
+ claim: "Reserve glow and drop-shadow for one or two focal moments per scene; a uniform glow on every panel is banned, frosted-glass surfaces are the sanctioned depth cue.",
4
+ why: "Uniform glows read as cheap template styling; de-glowing was part of every de-AI-ification round in the reels.",
5
+ learnedFrom: "claude-sessions-digest/portfolio-reels.md",
6
+ tier: "craft",
7
+ exceptions: [
8
+ {
9
+ project: "qse-qguard-video",
10
+ decision: "client chose ACT854's breathing accent bloom on every panel and glyph tile for the QGuard restyle; the bloom lives on rings and tiles, never behind body copy",
11
+ recordedIn: "qse-qguard-video/docs/motion-brand-spec.md"
12
+ }
13
+ ],
14
+ scope: { kind: "pipeline" },
15
+ enforcement: {
16
+ kind: "prose",
17
+ unanchorable: "The rule is a styling-restraint judgment across scenes; no single code location expresses it."
18
+ }
19
+ };
20
+ export {
21
+ glowIsAFocalAccent
22
+ };