@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,458 @@
1
+ import React, { useMemo } from "react";
2
+ import { AbsoluteFill, useCurrentFrame, spring, interpolate, interpolateColors, Easing } from "remotion";
3
+ import { FrameCam, CinematicStage, DustMotes, RingPulse, punchScale, rgba, type CamKey } from "@revola/video-kit/craft";
4
+ import { RevolaWatermark, spr, lerp, window4, SPRING } from "@revola/video-kit/react";
5
+ import {
6
+ windowFor,
7
+ cue,
8
+ expectCues,
9
+ WALL,
10
+ LIT_AT_OPEN,
11
+ DOCK_CELLS,
12
+ cellX,
13
+ cellY,
14
+ ZONE,
15
+ FOOTER_TEXT_X,
16
+ FPS,
17
+ } from "../layout";
18
+ import { EX } from "../tokens";
19
+ import { TIER } from "../text";
20
+ import { geometry } from "./scene.mechanism.geometry";
21
+
22
+ expectCues("mechanism", 4);
23
+
24
+ // VERTICAL ZONE MAP (declare-a-vertical-zone-map, values from ZONE in
25
+ // src/layout.ts):
26
+ // copy band ZONE.copy - HUD kicker + headline, SCREEN SPACE only.
27
+ // breath band ZONE.breath - nothing enters, ever.
28
+ // stage band ZONE.stage - the persistent GPU wall, the job ticket, the
29
+ // hex mark, the dock ignition. Camera keys below
30
+ // are chosen so the world never rises past the
31
+ // breath band at any key's extreme.
32
+ // footer band ZONE.footer - footnote (left) + Revola watermark (right),
33
+ // SCREEN SPACE only.
34
+
35
+ const SEC = "mechanism" as const;
36
+ if (geometry.sectionId !== SEC) {
37
+ throw new Error(`mechanism: geometry sidecar declares "${geometry.sectionId}", scene renders "${SEC}"`);
38
+ }
39
+
40
+ // The ticket's authored (design-space) size and its two flight stations.
41
+ const TICKET_W = 340;
42
+ const TICKET_H = 224;
43
+ const STATION = { x: 1400, y: 520 } as const; // scan station, right third of the stage band
44
+ const ENTER = { x: 2320, y: 430 } as const; // off-frame right, above the station
45
+
46
+ const WALL_W = WALL.cols * WALL.cellW + (WALL.cols - 1) * WALL.gapX;
47
+ const WALL_H = WALL.rows * WALL.cellH + (WALL.rows - 1) * WALL.gapY;
48
+
49
+ // The single receiving slot: the ticket docks flush into the first dark cell
50
+ // of the cluster; the remaining cluster cells ignite in cascade from it.
51
+ const SLOT = DOCK_CELLS[0];
52
+ const slotCx = cellX(SLOT) + WALL.cellW / 2;
53
+ const slotCy = cellY(SLOT) + WALL.cellH / 2;
54
+
55
+ /** The Expanse hex-cluster mark, hand-authored from the committed brand SVG
56
+ * (02_assets/brand/expanse-logo.svg, wheel only, no wordmark) so its spin-in
57
+ * runs on the frame clock instead of the source file's CSS keyframes
58
+ * (deterministic-scene-source). */
59
+ const HexMark: React.FC<{ size: number; ink: string }> = ({ size, ink }) => (
60
+ <svg width={size} height={size} viewBox="0 0 100 100">
61
+ <circle cx="50" cy="50" r="35" stroke={ink} strokeWidth="4.5" fill="none" />
62
+ {[0, 60, 120, 180, 240, 300].map((deg) => (
63
+ <g key={deg} transform={`rotate(${deg} 50 50)`}>
64
+ <line x1="50" y1="50" x2="50" y2="15" stroke={ink} strokeWidth="4.5" />
65
+ <circle cx="50" cy="15" r="8" fill={ink} />
66
+ </g>
67
+ ))}
68
+ <circle cx="50" cy="50" r="16" fill={ink} />
69
+ </svg>
70
+ );
71
+
72
+ /** One wall cell. Same grid constants as every scene (the persistent set);
73
+ * dock-cluster cells are the dark material the ticket lands in, and ignite
74
+ * coral in a cascade after contact. */
75
+ const Cell: React.FC<{ i: number; lf: number; landF: number }> = ({ i, lf, landF }) => {
76
+ const isLit = LIT_AT_OPEN.includes(i);
77
+ const dockIdx = DOCK_CELLS.indexOf(i);
78
+ const isDock = dockIdx >= 0;
79
+
80
+ const ignite = isDock
81
+ ? spring({ frame: Math.max(0, lf - landF - dockIdx * 4), fps: FPS, config: { damping: 13, mass: 0.7 } })
82
+ : 0;
83
+
84
+ // Idle living motion on lit cells: a slow phase-offset shimmer, seeded by
85
+ // index (no-static-holds without a second competing subject).
86
+ const shimmer = isLit ? 0.82 + 0.18 * Math.sin(lf * 0.06 + i * 1.7) : 0;
87
+
88
+ const base = isDock ? interpolateColors(ignite, [0, 1], [EX.buttonBg, EX.coral]) : isLit ? EX.coral : EX.cellUnlit;
89
+ const punch = isDock ? 1 + Math.sin(Math.min(1, ignite) * Math.PI) * 0.07 : 1;
90
+
91
+ return (
92
+ <div
93
+ style={{
94
+ position: "absolute",
95
+ left: cellX(i),
96
+ top: cellY(i),
97
+ width: WALL.cellW,
98
+ height: WALL.cellH,
99
+ transform: `scale(${punch})`,
100
+ borderRadius: 8,
101
+ backgroundColor: base,
102
+ border: `1px solid ${isLit || ignite > 0.3 ? rgba(EX.white, 0.45) : EX.hairline}`,
103
+ boxShadow:
104
+ ignite > 0.05
105
+ ? `0 10px 26px ${rgba(EX.ink, 0.16)}, 0 0 ${26 * ignite}px ${rgba(EX.coral, 0.55 * ignite)}`
106
+ : `0 10px 26px ${rgba(EX.ink, 0.1)}`,
107
+ }}
108
+ >
109
+ {/* Board detailing: dashed carrier outline, corner standoffs, die. */}
110
+ <svg width="100%" height="100%" style={{ position: "absolute", inset: 0, opacity: isLit ? 0.75 * shimmer : isDock ? 0.5 : 0.35 }}>
111
+ <rect
112
+ x="8"
113
+ y="8"
114
+ width={WALL.cellW - 16}
115
+ height={WALL.cellH - 16}
116
+ rx="3"
117
+ stroke={isLit || ignite > 0.3 ? EX.white : EX.inkMuted}
118
+ fill="none"
119
+ strokeWidth="1"
120
+ strokeDasharray="2 3"
121
+ />
122
+ {[
123
+ [14, 14],
124
+ [WALL.cellW - 14, 14],
125
+ [14, WALL.cellH - 14],
126
+ [WALL.cellW - 14, WALL.cellH - 14],
127
+ ].map(([x, y]) => (
128
+ <circle key={`${x}-${y}`} cx={x} cy={y} r="2" fill={isLit || ignite > 0.3 ? EX.white : EX.inkMuted} />
129
+ ))}
130
+ <rect
131
+ x={WALL.cellW / 2 - 34}
132
+ y={WALL.cellH / 2 - 26}
133
+ width={68}
134
+ height={52}
135
+ rx="3"
136
+ fill={isLit || ignite > 0.3 ? rgba(EX.white, 0.25) : rgba(EX.ink, 0.06)}
137
+ />
138
+ </svg>
139
+ {/* Top sheen so the slab reads as material, not flat fill. */}
140
+ <div
141
+ style={{
142
+ position: "absolute",
143
+ inset: 0,
144
+ borderRadius: 8,
145
+ background: `linear-gradient(180deg, ${rgba(EX.white, isLit ? 0.22 : 0.4)} 0%, transparent 38%)`,
146
+ }}
147
+ />
148
+ </div>
149
+ );
150
+ };
151
+
152
+ export const S2Mechanism: React.FC = () => {
153
+ const frame = useCurrentFrame();
154
+ const win = windowFor(SEC);
155
+ const lf = frame - win.from;
156
+
157
+ const c0 = cue(SEC, 0); // "Expanse predicts resource fit and failure risk"
158
+ const c1 = cue(SEC, 1); // "the" (breath cue) - hex mark docks, scan starts
159
+ const c2 = cue(SEC, 2); // "moment a job is submitted" - readouts + T+0ms
160
+ const c3 = cue(SEC, 3); // "so more work lands on the same hardware"
161
+ const endF = win.durationInFrames;
162
+
163
+ // ---- job-ticket flight, evaluated as a pure function of frame so motion
164
+ // blur samples the PREVIOUS frame's real position, never a curve-parameter
165
+ // delta (sample-velocity-from-previous-frame-position).
166
+ // Negative predraw start (predraw-content-present-at-the-cut): the elegant
167
+ // spring is slow and heavy, so the flight begins well before the window and
168
+ // the ticket is docked at the station, fully settled and inside the frame,
169
+ // before its own sentence (cue 0) starts speaking.
170
+ const tEnter = c0 - 64;
171
+ const dockScale = WALL.cellW / TICKET_W;
172
+ const ticketAt = (f: number) => {
173
+ const e = spr(f, tEnter, FPS, SPRING.elegant);
174
+ const d = spr(f, c3, FPS, SPRING.elegant);
175
+ const x = STATION.x + (1 - e) * (ENTER.x - STATION.x) + d * (slotCx - STATION.x);
176
+ const y =
177
+ STATION.y +
178
+ (1 - e) * (ENTER.y - STATION.y) -
179
+ Math.sin(e * Math.PI) * 70 +
180
+ d * (slotCy - STATION.y) -
181
+ Math.sin(d * Math.PI) * 120;
182
+ const rot = (1 - e) * 9 - Math.sin(d * Math.PI) * 6;
183
+ const scale = (0.92 + 0.08 * e) * (1 + d * (dockScale - 1));
184
+ return { x, y, rot, scale, d };
185
+ };
186
+ const tk = ticketAt(lf);
187
+ const tkPrev = ticketAt(lf - 1);
188
+ const tkSpeed = Math.hypot(tk.x - tkPrev.x, tk.y - tkPrev.y);
189
+ const tkBlur = tkSpeed > 16 ? Math.min(3.5, (tkSpeed - 16) * 0.09) : 0;
190
+
191
+ // Contact: the frame the dock spring has essentially landed. Squash +
192
+ // ring pulse + cascade all key off this one moment (impact-stack-on-payoffs).
193
+ const landF = c3 + 14;
194
+ const landSquash = punchScale(lf, landF, 0.06, 9);
195
+ // After contact the ticket fuses into the slot cell: it fades as the cell
196
+ // ignites, so the clicked object literally BECOMES the set's next state.
197
+ const tkFade = interpolate(lf, [landF + 4, landF + 16], [1, 0], {
198
+ extrapolateLeft: "clamp",
199
+ extrapolateRight: "clamp",
200
+ easing: Easing.inOut(Easing.quad),
201
+ });
202
+
203
+ // ---- hex mark: spins in beside the ticket on cue 1, then holds while the
204
+ // scanline it drives crosses the card.
205
+ // Enter early enough (overlapping cue 0) that the snappy spring is fully
206
+ // settled and the mark at opacity 1 before its breath cue (cue 1) begins.
207
+ const hex = spr(lf, c1 - 22, FPS, SPRING.snappy);
208
+ const hexSpin = (1 - hex) * -240;
209
+
210
+ // ---- scanline across the card (the scene's one focal glow, with the dock
211
+ // ignition: glow-is-a-focal-accent).
212
+ const scanT = interpolate(lf, [c1, c1 + 26], [0, 1], {
213
+ extrapolateLeft: "clamp",
214
+ extrapolateRight: "clamp",
215
+ easing: Easing.inOut(Easing.cubic),
216
+ });
217
+ const scanAlpha = window4(lf, c1 - 2, c1 + 4, c1 + 22, c1 + 30);
218
+
219
+ // ---- readout typing, staggered mono rows on cue 2.
220
+ const row1 = "RESOURCE FIT · PREDICTED";
221
+ const row2 = "FAILURE RISK · LOW";
222
+ // Typing overlaps the scan beat (lateness-is-structural-overlap-the-beats)
223
+ // so both rows are fully typed, opacity 1 and settled before cue 2 - the
224
+ // sentence that names the readout - starts speaking.
225
+ const typed1 = row1.slice(0, Math.round(lerp(lf, [c2 - 26, c2 - 10], [0, row1.length])));
226
+ const typed2 = row2.slice(0, Math.round(lerp(lf, [c2 - 20, c2 - 4], [0, row2.length])));
227
+ const rowSettled1 = spr(lf, c2 - 26, FPS, SPRING.snappy);
228
+ const rowSettled2 = spr(lf, c2 - 20, FPS, SPRING.snappy);
229
+
230
+ // ---- T+0ms stamp on the spoken submission beat (cue 2), gone by dock.
231
+ const stampIn = spr(lf, c2, FPS, SPRING.bouncy);
232
+ const stampOut = interpolate(lf, [c3, c3 + 8], [1, 0], {
233
+ extrapolateLeft: "clamp",
234
+ extrapolateRight: "clamp",
235
+ });
236
+
237
+ // ---- camera: open static (two equal keys), one committed push into the
238
+ // ticket while it is scanned (dwell), one follow to the slot, then the
239
+ // pull-back that unlock inherits (camera-is-the-narrator).
240
+ const camKeys: CamKey[] = [
241
+ { f: 0, scale: 1.24, cx: WALL.x + WALL_W / 2 + 180, cy: 560 },
242
+ { f: 12, scale: 1.24, cx: WALL.x + WALL_W / 2 + 180, cy: 560 },
243
+ { f: c1, scale: 1.52, cx: STATION.x - 70, cy: STATION.y + 40 },
244
+ { f: c3, scale: 1.52, cx: STATION.x - 70, cy: STATION.y + 40 },
245
+ { f: landF + 6, scale: 1.66, cx: slotCx + 60, cy: slotCy - 10 },
246
+ { f: endF, scale: 1.3, cx: WALL.x + WALL_W / 2, cy: WALL.y + WALL_H / 2 + 60 },
247
+ ];
248
+
249
+ const cells = useMemo(() => Array.from({ length: WALL.cols * WALL.rows }, (_, i) => i), []);
250
+
251
+ // Ruled body lines of the manifest (tiny-body-copy-as-ruled-lines), widths
252
+ // seeded by index - real text is reserved for the header and readout rows.
253
+ const ruled = [0.94, 0.72, 0.86, 0.6, 0.78];
254
+
255
+ return (
256
+ <AbsoluteFill style={{ backgroundColor: EX.paper }}>
257
+ <FrameCam frame={lf} keys={camKeys} breathe>
258
+ <CinematicStage frame={lf} base={EX.paper} warmth={0.45} grid={false} />
259
+ <DustMotes frame={lf} color={EX.inkMuted} />
260
+
261
+ {/* The persistent GPU wall - on screen at frame 0, no re-entry. */}
262
+ <div
263
+ style={{
264
+ position: "absolute",
265
+ left: 0,
266
+ top: 0,
267
+ width: 1920,
268
+ height: 1080,
269
+ }}
270
+ >
271
+ {/* Long-throw ground shadow under the whole cluster. */}
272
+ <div
273
+ style={{
274
+ position: "absolute",
275
+ left: WALL.x - 50,
276
+ top: WALL.y - 30,
277
+ width: WALL_W + 100,
278
+ height: WALL_H + 110,
279
+ background: `radial-gradient(ellipse at 50% 55%, ${rgba(EX.ink, 0.1)} 0%, transparent 70%)`,
280
+ }}
281
+ />
282
+ {cells.map((i) => (
283
+ <Cell key={i} i={i} lf={lf} landF={landF} />
284
+ ))}
285
+ <RingPulse frame={lf} at={landF} x={slotCx} y={slotCy} maxRadius={260} color={rgba(EX.coral, 0.8)} />
286
+ </div>
287
+
288
+ {/* THE JOB TICKET - flies in from off-frame right, is scanned, then
289
+ slots flush into the dark cluster. */}
290
+ <div
291
+ data-radar="job-ticket"
292
+ style={{
293
+ position: "absolute",
294
+ left: tk.x - TICKET_W / 2,
295
+ top: tk.y - TICKET_H / 2,
296
+ width: TICKET_W,
297
+ height: TICKET_H,
298
+ transform: `rotate(${tk.rot}deg) scale(${tk.scale * landSquash})`,
299
+ transformOrigin: "50% 50%",
300
+ opacity: tkFade,
301
+ borderRadius: 14,
302
+ backgroundColor: EX.white,
303
+ // Premium layered surface: contact + ambient shadow, hairline
304
+ // ring; the top sheen is the gradient div below. Matte white -
305
+ // the glow budget belongs to the scanline and the ignition.
306
+ boxShadow: `0 2px 4px ${rgba(EX.ink, 0.08)}, 0 24px 48px ${rgba(EX.ink, 0.16)}, 0 0 0 1px ${EX.hairline}`,
307
+ filter: tkBlur > 0.3 ? `blur(${tkBlur}px)` : "none",
308
+ overflow: "hidden",
309
+ }}
310
+ >
311
+ <div
312
+ style={{
313
+ position: "absolute",
314
+ inset: 0,
315
+ borderRadius: 14,
316
+ background: `linear-gradient(175deg, ${rgba(EX.white, 0.9)} 0%, transparent 30%)`,
317
+ pointerEvents: "none",
318
+ }}
319
+ />
320
+ <div style={{ position: "absolute", inset: 0, padding: 22, display: "flex", flexDirection: "column" }}>
321
+ <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between" }}>
322
+ <div style={{ ...TIER.label, fontSize: 17, color: EX.ink }}>JOB · SUBMIT</div>
323
+ <div style={{ ...TIER.label, fontSize: 13, color: EX.inkMuted }}>QUEUE · TRAIN</div>
324
+ </div>
325
+ {/* Manifest body: fine ruled lines only. */}
326
+ <div style={{ display: "flex", flexDirection: "column", gap: 11, marginTop: 20, flex: 1 }}>
327
+ {ruled.map((w, i) => (
328
+ <div
329
+ key={i}
330
+ style={{
331
+ height: 3,
332
+ width: `${w * 100}%`,
333
+ borderRadius: 2,
334
+ backgroundColor: EX.cellUnlit,
335
+ }}
336
+ />
337
+ ))}
338
+ </div>
339
+ {/* The two prediction readouts, typed on in mono at cue 2. */}
340
+ <div data-radar="fit-risk-readout" style={{ display: "flex", flexDirection: "column", gap: 7 }}>
341
+ <div
342
+ style={{
343
+ ...TIER.label,
344
+ fontSize: 15,
345
+ color: EX.ink,
346
+ opacity: Math.min(1, rowSettled1 * 1.4),
347
+ transform: `translateY(${(1 - rowSettled1) * 6}px)`,
348
+ }}
349
+ >
350
+ <span style={{ color: EX.coral }}>●</span> {typed1}
351
+ </div>
352
+ <div
353
+ style={{
354
+ ...TIER.label,
355
+ fontSize: 15,
356
+ color: EX.ink,
357
+ opacity: Math.min(1, rowSettled2 * 1.4),
358
+ transform: `translateY(${(1 - rowSettled2) * 6}px)`,
359
+ }}
360
+ >
361
+ <span style={{ color: EX.coral }}>●</span> {typed2}
362
+ </div>
363
+ </div>
364
+ </div>
365
+ {/* The coral scanline the hex mark drives across the card. */}
366
+ {scanAlpha > 0 && (
367
+ <div
368
+ style={{
369
+ position: "absolute",
370
+ left: 0,
371
+ right: 0,
372
+ top: `${scanT * 100}%`,
373
+ height: 3,
374
+ backgroundColor: EX.coral,
375
+ boxShadow: `0 0 14px 2px ${rgba(EX.coral, 0.7)}`,
376
+ opacity: scanAlpha,
377
+ }}
378
+ />
379
+ )}
380
+ {scanAlpha > 0 && (
381
+ <div
382
+ style={{
383
+ position: "absolute",
384
+ left: 0,
385
+ right: 0,
386
+ top: 0,
387
+ height: `${scanT * 100}%`,
388
+ background: `linear-gradient(180deg, transparent 60%, ${rgba(EX.coral, 0.08)} 100%)`,
389
+ opacity: scanAlpha,
390
+ }}
391
+ />
392
+ )}
393
+ </div>
394
+
395
+ {/* The hex-cluster mark docks beside the ticket and scans it. It
396
+ rides the ticket's flight offset so the pair reads as one rig. */}
397
+ <div
398
+ data-radar="expanse-hex-mark"
399
+ style={{
400
+ position: "absolute",
401
+ left: tk.x - TICKET_W / 2 - 76,
402
+ top: tk.y - TICKET_H / 2 - 8,
403
+ width: 60,
404
+ height: 60,
405
+ opacity: Math.min(1, hex * 1.5) * tkFade,
406
+ transform: `rotate(${hexSpin}deg) scale(${(0.7 + 0.3 * hex) * tk.scale})`,
407
+ transformOrigin: "50% 50%",
408
+ }}
409
+ >
410
+ <HexMark size={60} ink={EX.ink} />
411
+ </div>
412
+
413
+ {/* T+0ms - the point IS submission time. Stamps on its spoken beat. */}
414
+ {stampIn > 0.01 && stampOut > 0.01 && (
415
+ <div
416
+ style={{
417
+ position: "absolute",
418
+ left: tk.x + TICKET_W / 2 + 18,
419
+ top: tk.y - 16,
420
+ transform: `scale(${stampIn})`,
421
+ transformOrigin: "0% 50%",
422
+ opacity: stampOut,
423
+ ...TIER.stat,
424
+ fontSize: 22,
425
+ color: EX.coral,
426
+ backgroundColor: EX.white,
427
+ padding: "6px 14px",
428
+ borderRadius: 8,
429
+ boxShadow: `0 6px 16px ${rgba(EX.ink, 0.12)}, 0 0 0 1px ${EX.hairline}`,
430
+ }}
431
+ >
432
+ T+0ms
433
+ </div>
434
+ )}
435
+ </FrameCam>
436
+
437
+ {/* HUD - screen space, outside the camera (titles-pinned-outside-camera-push). */}
438
+ <div style={{ position: "absolute", inset: 0, pointerEvents: "none" }}>
439
+ <div style={{ position: "absolute", left: 60, top: 60 }}>
440
+ <div style={{ ...TIER.label, color: EX.inkMuted, marginBottom: 10 }}>02/03 - THE MECHANISM</div>
441
+ <div style={{ ...TIER.label, fontSize: 24, color: EX.ink }}>PREDICT AT SUBMISSION</div>
442
+ </div>
443
+
444
+ <div style={{ position: "absolute", left: FOOTER_TEXT_X.left, top: ZONE.footer.top + 30 }}>
445
+ <div style={{ ...TIER.footnote, color: EX.inkMuted }}>
446
+ Expanse - cluster intelligence at job-submission time.
447
+ </div>
448
+ </div>
449
+
450
+ <div style={{ position: "absolute", left: FOOTER_TEXT_X.right, top: ZONE.footer.top + 10 }}>
451
+ <RevolaWatermark tone="onLight" />
452
+ </div>
453
+ </div>
454
+ </AbsoluteFill>
455
+ );
456
+ };
457
+
458
+ export default S2Mechanism;
@@ -0,0 +1,15 @@
1
+ import React from "react";
2
+ import { useCurrentFrame } from "remotion";
3
+ import { SECTION_WINDOWS, windowFor } from "./layout";
4
+
5
+ // Mutation: the `from` read hides inside helper wrappers - one over
6
+ // windowFor(), one over SECTION_WINDOWS - and call sites only see a clean
7
+ // function name.
8
+ const sectionStart = () => windowFor("mechanism").from;
9
+ const firstStart = () => SECTION_WINDOWS[0].from;
10
+
11
+ export const Wrapped: React.FC = () => {
12
+ const frame = useCurrentFrame();
13
+ const lf = frame - sectionStart() + firstStart();
14
+ return <div style={{ opacity: lf > 0 ? 1 : 0 }} />;
15
+ };
@@ -0,0 +1,10 @@
1
+ // Missing violation: no `geometry` export at all, e.g. a sidecar file that
2
+ // was scaffolded but never filled in, or a helper named something else.
3
+ import type { SceneGeometry } from "@revola/video-kit/pure";
4
+
5
+ const localOnly: SceneGeometry = {
6
+ sectionId: "once",
7
+ exit: { object: "instrument-card", x: 960, y: 540, scale: 1.18 },
8
+ };
9
+
10
+ export default localOnly;
@@ -0,0 +1,7 @@
1
+ // Missing violation: exports `geometry`, but with no `SceneGeometry` type
2
+ // annotation, so a shape drift at a call site would not be caught at compile
3
+ // time.
4
+ export const geometry = {
5
+ sectionId: "once",
6
+ exit: { object: "instrument-card", x: 960, y: 540, scale: 1.18 },
7
+ };
@@ -0,0 +1,6 @@
1
+ import type { SceneGeometry } from "@revola/video-kit/pure";
2
+
3
+ export const geometry: SceneGeometry = {
4
+ sectionId: "once",
5
+ exit: { object: "instrument-card", x: 960, y: 540, scale: 1.18 },
6
+ };
@@ -0,0 +1,6 @@
1
+ // Strictly ascending literal ranges, including a negative start: clean.
2
+ declare const interpolate: (frame: number, input: number[], output: number[]) => number;
3
+ declare const frame: number;
4
+
5
+ export const x = interpolate(frame, [0, 134, 136, 200], [0, 1, 1, 0]);
6
+ export const y = interpolate(frame, [-20, -10, 0], [0, 0.5, 1]);
@@ -0,0 +1,8 @@
1
+ // A literal input range that is not strictly ascending: interpolate() throws
2
+ // at runtime when this frame range is reached. The BUILD_NOTES_CRAFT case:
3
+ // HERO_AT (134) retimed earlier than TILT_END (136) in a literal key array.
4
+ declare const interpolate: (frame: number, input: number[], output: number[]) => number;
5
+ declare const frame: number;
6
+
7
+ export const x = interpolate(frame, [0, 136, 134, 200], [0, 1, 1, 0]);
8
+ export const y = interpolate(frame, [-10, -20], [0, 1]);
@@ -0,0 +1,9 @@
1
+ // Identifier-composed ranges are out of scope: proving them monotonic would
2
+ // need constant folding across declarations, so the check stays literal-only.
3
+ declare const interpolate: (frame: number, input: number[], output: number[]) => number;
4
+ declare const frame: number;
5
+
6
+ const TILT_END = 136;
7
+ const HERO_AT = 134;
8
+
9
+ export const x = interpolate(frame, [0, TILT_END, HERO_AT, 200], [0, 1, 1, 0]);
@@ -0,0 +1,11 @@
1
+ // Reproduction of the bug in revola-video BUILD_NOTES_CRAFT.md, S2 pass 2:
2
+ // "a CSS-filtered <g> wrapping children that ALSO have CSS filters makes Chrome
3
+ // drop those children silently - the first cursor build rendered nothing while
4
+ // its group rendered fine".
5
+ export const Cursor = () => (
6
+ <svg viewBox="0 0 100 100">
7
+ <g style={{ filter: "drop-shadow(0 0 6px #9C33FF)" }}>
8
+ <path d="M0 0 L10 30" style={{ filter: "blur(2px)" }} />
9
+ </g>
10
+ </svg>
11
+ );
@@ -0,0 +1,8 @@
1
+ // The fix: one flat filter on one layer.
2
+ export const Cursor = () => (
3
+ <svg viewBox="0 0 100 100">
4
+ <g style={{ filter: "drop-shadow(0 0 6px #9C33FF)" }}>
5
+ <path d="M0 0 L10 30" />
6
+ </g>
7
+ </svg>
8
+ );
@@ -0,0 +1,14 @@
1
+ // The false-positive shape corpus calibration hit three times (Scene4_Mechanism,
2
+ // S2Turn, S4Proof): a CSS-filtered HTML wrapper around a whole <svg> whose child
3
+ // carries its own filter. The wrapper's filter composites the rendered svg at the
4
+ // HTML level, so nothing is dropped - all three shipped scenes render correctly.
5
+ // Only a filtered ancestor INSIDE the <svg> subtree triggers the Chrome bug.
6
+ export const Hero = () => (
7
+ <div style={{ filter: "blur(4px)" }}>
8
+ <svg viewBox="0 0 100 100">
9
+ <text x="50" y="50" style={{ filter: "drop-shadow(0 0 6px #9C33FF)" }}>
10
+ hi
11
+ </text>
12
+ </svg>
13
+ </div>
14
+ );
@@ -0,0 +1,7 @@
1
+ // Nested CSS filters in plain HTML render fine - the silent-drop bug is
2
+ // SVG-specific. The rule must not fire outside an <svg>.
3
+ export const Card = () => (
4
+ <div style={{ filter: "drop-shadow(0 0 6px #9C33FF)" }}>
5
+ <span style={{ filter: "blur(2px)" }}>hello</span>
6
+ </div>
7
+ );
@@ -0,0 +1,12 @@
1
+ // Two filtered SIBLINGS are fine - only a filtered ancestor wrapping a
2
+ // filtered descendant triggers the Chrome bug. This fixture guards the
3
+ // most likely false-positive shape: the check matching an element against
4
+ // itself or against a non-ancestor.
5
+ export const Cursor = () => (
6
+ <svg viewBox="0 0 100 100">
7
+ <g>
8
+ <path d="M0 0 L10 30" style={{ filter: "blur(2px)" }} />
9
+ <path d="M10 0 L20 30" style={{ filter: "drop-shadow(0 0 6px #9C33FF)" }} />
10
+ </g>
11
+ </svg>
12
+ );
@@ -0,0 +1,14 @@
1
+ import React from "react";
2
+
3
+ export const Jitter: React.FC = () => {
4
+ const seed = Math.random();
5
+ const now = Date.now();
6
+ const stamp = new Date();
7
+ const bare = new Date; // no parens: still an argless construction
8
+ void bare;
9
+ return (
10
+ <div style={{ opacity: seed }}>
11
+ {now} {stamp.toISOString()}
12
+ </div>
13
+ );
14
+ };
@@ -0,0 +1,16 @@
1
+ import React from "react";
2
+
3
+ // Evasion spellings the check deliberately does NOT flag (see the rule's
4
+ // "Deliberate limitation" note). This fixture pins that boundary: if the
5
+ // check is ever extended to resolve these, the companion test must flip.
6
+ const M = Math;
7
+ const { random } = Math;
8
+
9
+ export const Variants: React.FC = () => {
10
+ const a = M.random();
11
+ const b = random();
12
+ const c = Math["random"]();
13
+ const d = globalThis.Date.now();
14
+ const e = new Date(undefined as unknown as number);
15
+ return <div>{a + b + c + d + e.getTime()}</div>;
16
+ };
@@ -0,0 +1,10 @@
1
+ // A scene that hard-codes palette hexes instead of referencing the token
2
+ // module. One in a plain string prop, one inside a template-literal gradient.
3
+ // The pure-white drop-shadow is NOT a violation: white/black glint constants
4
+ // are not palette (the dark regrade shipped with them unchanged).
5
+ export const Panel = () => (
6
+ <div style={{ background: "#0E1420" }}>
7
+ <div style={{ backgroundImage: `linear-gradient(180deg, #9C33FF 0%, transparent 100%)` }} />
8
+ <div style={{ boxShadow: "0 0 12px #fff", borderColor: "#000000" }} />
9
+ </div>
10
+ );
@@ -0,0 +1,9 @@
1
+ // The convention: no whole-scene camera transform, so the root AbsoluteFill
2
+ // declares data-radar-scene-root="static".
3
+ export default function SceneHook() {
4
+ return (
5
+ <div data-radar-scene-root="static" style={{ position: "absolute", inset: 0 }}>
6
+ <div data-radar="hero" style={{ width: 900, height: 500 }} />
7
+ </div>
8
+ );
9
+ }
@@ -0,0 +1,10 @@
1
+ doctrine: 1
2
+ sections:
3
+ - id: hook
4
+ kind: motion
5
+ component: SceneHook
6
+ vo: "The hero image opens the film."
7
+ names:
8
+ - cue: 0
9
+ element: hero
10
+ says: "the hero image"
@@ -0,0 +1,11 @@
1
+ // A radar-adopting scene (its plan declares a names: binding) whose source
2
+ // nowhere carries the scene-space contract tag: radar:scene refuses this at
3
+ // setup. (The tag's literal name must not appear even in a comment here -
4
+ // the check is textual containment over the whole project's source.)
5
+ export default function SceneHook() {
6
+ return (
7
+ <div style={{ position: "absolute", inset: 0 }}>
8
+ <div data-radar="hero" style={{ width: 900, height: 500 }} />
9
+ </div>
10
+ );
11
+ }