m0saic 0.2.2 → 0.3.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 (441) hide show
  1. package/LICENSE +22 -7
  2. package/README.md +23 -5
  3. package/dist/browseE2e.js +1 -1
  4. package/dist/connectionsFromEnv.js +1 -1
  5. package/dist/e2eTemplateVariants.js +1 -1
  6. package/dist/frontDoor.js +1 -1
  7. package/dist/index.js +1 -1
  8. package/dist/inlineDsl.js +1 -1
  9. package/dist/mcp/bridgeClient.js +1 -0
  10. package/dist/mcp/server.js +1 -0
  11. package/dist/mcp/tools.js +1 -0
  12. package/dist/mosaicxRunner.js +1 -1
  13. package/dist/outputPattern.js +1 -1
  14. package/dist/showcaseFeed.js +1 -1
  15. package/dist/templateDoctor.js +1 -1
  16. package/dist/utils/checkFeasibility.js +1 -1
  17. package/dist/utils/collectRenderErrors.js +1 -1
  18. package/dist/utils/enrichImageAlphaFromDoc.js +1 -1
  19. package/dist/utils/ensureFfmpeg.js +1 -1
  20. package/dist/utils/extractLayoutMeta.js +1 -1
  21. package/dist/utils/failureHints.js +1 -1
  22. package/dist/utils/ffmpegProbe.js +1 -1
  23. package/dist/utils/formatM0saicForCli.js +1 -1
  24. package/dist/utils/hashFile.js +1 -1
  25. package/dist/utils/helpGroups.js +1 -1
  26. package/dist/utils/installContext.js +1 -0
  27. package/dist/utils/invocation.js +1 -1
  28. package/dist/utils/licenseActivation.js +1 -0
  29. package/dist/utils/licenseActivationWorker.js +1 -0
  30. package/dist/utils/mosaicSpinner.js +1 -1
  31. package/dist/utils/parseDimensions.js +1 -1
  32. package/dist/utils/pathInfo.js +1 -1
  33. package/dist/utils/readJsonArg.js +1 -1
  34. package/dist/utils/renderOutcome.js +1 -1
  35. package/dist/utils/renderReport.js +1 -1
  36. package/dist/utils/renderSignals.js +1 -1
  37. package/dist/utils/renderWorkspace.js +1 -1
  38. package/dist/utils/reportOutputFormat.js +1 -0
  39. package/dist/utils/setupFfmpeg.js +1 -1
  40. package/dist/utils/telemetry.js +1 -1
  41. package/dist/utils/telemetryFlushWorker.js +1 -1
  42. package/dist/utils/telemetryUpstream.js +1 -1
  43. package/dist/utils/telemetryUsage.js +1 -1
  44. package/dist/utils/toolchainConfig.js +1 -1
  45. package/dist/utils/tutorialDuration.js +1 -1
  46. package/dist/utils/updateCheck.js +1 -1
  47. package/dist/utils/updateCheckWorker.js +1 -1
  48. package/dist/utils/validateSourceCounts.js +1 -1
  49. package/dist/utils/versions.js +1 -1
  50. package/dist/webCustomizeLink.js +1 -1
  51. package/node_modules/@m0saic/community-m/NOTICE +9 -0
  52. package/node_modules/@m0saic/community-m/release.json +2 -2
  53. package/node_modules/@m0saic/core/dist/core/buildChildMosaicSource.js +1 -1
  54. package/node_modules/@m0saic/core/dist/core/buildChunkCommands.js +1 -1
  55. package/node_modules/@m0saic/core/dist/core/buildEncodeCommands.js +1 -1
  56. package/node_modules/@m0saic/core/dist/core/buildLavfiSource.js +1 -1
  57. package/node_modules/@m0saic/core/dist/core/buildMediaSource.js +1 -1
  58. package/node_modules/@m0saic/core/dist/core/buildMosaicNode.js +1 -1
  59. package/node_modules/@m0saic/core/dist/core/buildMosaicPlanFromFile.js +1 -1
  60. package/node_modules/@m0saic/core/dist/core/buildSvgTextSource.js +1 -1
  61. package/node_modules/@m0saic/core/dist/core/buildTextRenderCommand.js +1 -1
  62. package/node_modules/@m0saic/core/dist/core/collectRefTargets.js +1 -1
  63. package/node_modules/@m0saic/core/dist/core/index.js +1 -1
  64. package/node_modules/@m0saic/core/dist/core/maskCacheDir.js +1 -1
  65. package/node_modules/@m0saic/core/dist/core/maskWorkerBridge.js +1 -1
  66. package/node_modules/@m0saic/core/dist/core/playbackPlaySpeed.js +1 -1
  67. package/node_modules/@m0saic/core/dist/core/prepareProps.js +1 -1
  68. package/node_modules/@m0saic/core/dist/core/primaryOutput.js +1 -1
  69. package/node_modules/@m0saic/core/dist/core/rasterizeMaskPng.js +1 -1
  70. package/node_modules/@m0saic/core/dist/core/rasterizeSvgMediaPng.js +1 -1
  71. package/node_modules/@m0saic/core/dist/core/refTargetTap.js +1 -1
  72. package/node_modules/@m0saic/core/dist/core/resolveMasks.js +1 -1
  73. package/node_modules/@m0saic/core/dist/core/resolveMasksSync.js +1 -1
  74. package/node_modules/@m0saic/core/dist/core/resolveStrokeRingsSync.js +1 -1
  75. package/node_modules/@m0saic/core/dist/core/roundedRectMask.js +1 -1
  76. package/node_modules/@m0saic/core/dist/core/strokeRing.js +1 -1
  77. package/node_modules/@m0saic/core/dist/core/svgRaster.js +1 -1
  78. package/node_modules/@m0saic/core/dist/defaults.js +1 -1
  79. package/node_modules/@m0saic/core/dist/ffmpeg/backgroundImageInput.js +1 -1
  80. package/node_modules/@m0saic/core/dist/ffmpeg/bufferGuard.js +1 -1
  81. package/node_modules/@m0saic/core/dist/ffmpeg/ffmpegCommands.js +1 -1
  82. package/node_modules/@m0saic/core/dist/ffmpeg/filters/applyBackdropBlur.js +1 -1
  83. package/node_modules/@m0saic/core/dist/ffmpeg/filters/applyBlur.js +1 -1
  84. package/node_modules/@m0saic/core/dist/ffmpeg/filters/applyBorderRadius.js +1 -1
  85. package/node_modules/@m0saic/core/dist/ffmpeg/filters/applyCamera.js +1 -1
  86. package/node_modules/@m0saic/core/dist/ffmpeg/filters/applyChromaKey.js +1 -1
  87. package/node_modules/@m0saic/core/dist/ffmpeg/filters/applyFade.js +1 -1
  88. package/node_modules/@m0saic/core/dist/ffmpeg/filters/applyGrade.js +1 -1
  89. package/node_modules/@m0saic/core/dist/ffmpeg/filters/applyMask.js +1 -1
  90. package/node_modules/@m0saic/core/dist/ffmpeg/filters/applyNoise.js +1 -1
  91. package/node_modules/@m0saic/core/dist/ffmpeg/filters/applyPixelize.js +1 -1
  92. package/node_modules/@m0saic/core/dist/ffmpeg/filters/applyRotate.js +1 -1
  93. package/node_modules/@m0saic/core/dist/ffmpeg/filters/applyStroke.js +1 -1
  94. package/node_modules/@m0saic/core/dist/ffmpeg/filters/applyTileEffects.js +1 -1
  95. package/node_modules/@m0saic/core/dist/ffmpeg/filters/coverCrop.js +1 -1
  96. package/node_modules/@m0saic/core/dist/ffmpeg/filters/index.js +1 -1
  97. package/node_modules/@m0saic/core/dist/ffmpeg/filters/sourceRectCrop.js +1 -1
  98. package/node_modules/@m0saic/core/dist/ffmpeg/gridLowering.js +1 -1
  99. package/node_modules/@m0saic/core/dist/ffmpeg/index.js +1 -1
  100. package/node_modules/@m0saic/core/dist/ffmpeg/splitAvMux.js +1 -1
  101. package/node_modules/@m0saic/core/dist/ffmpeg/threadGuard.js +1 -1
  102. package/node_modules/@m0saic/core/dist/flatten/index.js +1 -1
  103. package/node_modules/@m0saic/core/dist/fs/index.js +1 -1
  104. package/node_modules/@m0saic/core/dist/fs/tempWorkspace.js +1 -1
  105. package/node_modules/@m0saic/core/dist/index.js +1 -1
  106. package/node_modules/@m0saic/core/dist/media/buildAssetManifestFromInputs.js +1 -1
  107. package/node_modules/@m0saic/core/dist/media/buildMediaRegistryFromInput.js +1 -1
  108. package/node_modules/@m0saic/core/dist/media/collectMediaInputsFromInvocationProps.js +1 -1
  109. package/node_modules/@m0saic/core/dist/media/collectMediaInputsFromMosaicRenderableFile.js +1 -1
  110. package/node_modules/@m0saic/core/dist/media/collectMediaInputsFromProps.js +1 -1
  111. package/node_modules/@m0saic/core/dist/media/index.js +1 -1
  112. package/node_modules/@m0saic/core/dist/media/internalMintAsset.js +1 -1
  113. package/node_modules/@m0saic/core/dist/media/remoteUrl.js +1 -1
  114. package/node_modules/@m0saic/core/dist/media/stageUnsafeFilenames.js +1 -1
  115. package/node_modules/@m0saic/core/dist/media/types.js +1 -1
  116. package/node_modules/@m0saic/core/dist/output/index.js +1 -1
  117. package/node_modules/@m0saic/core/dist/output/resolveOutputFormat.js +1 -1
  118. package/node_modules/@m0saic/core/dist/output/types.js +1 -1
  119. package/node_modules/@m0saic/core/dist/probe/cellLuminanceProbe.js +1 -1
  120. package/node_modules/@m0saic/core/dist/probe/extractSubtitleCues.js +1 -1
  121. package/node_modules/@m0saic/core/dist/probe/helpers.js +1 -1
  122. package/node_modules/@m0saic/core/dist/probe/index.js +1 -1
  123. package/node_modules/@m0saic/core/dist/probe/luminanceProbe.js +1 -1
  124. package/node_modules/@m0saic/core/dist/probe/probeMedia.js +1 -1
  125. package/node_modules/@m0saic/core/dist/probe/runFFprobe.js +1 -1
  126. package/node_modules/@m0saic/core/dist/probe/types.js +1 -1
  127. package/node_modules/@m0saic/core/dist/runtime/engineRedaction.js +1 -1
  128. package/node_modules/@m0saic/core/dist/runtime/ffmpegFailure.js +1 -1
  129. package/node_modules/@m0saic/core/dist/runtime/filterPreflight.js +1 -1
  130. package/node_modules/@m0saic/core/dist/runtime/hashFile.js +1 -1
  131. package/node_modules/@m0saic/core/dist/runtime/index.js +1 -1
  132. package/node_modules/@m0saic/core/dist/runtime/pathInfo.js +1 -1
  133. package/node_modules/@m0saic/core/dist/runtime/perfHeatmap.js +1 -1
  134. package/node_modules/@m0saic/core/dist/runtime/perfReport.js +1 -1
  135. package/node_modules/@m0saic/core/dist/runtime/pipelineM0p.js +1 -1
  136. package/node_modules/@m0saic/core/dist/runtime/renderCostModel.constants.js +1 -1
  137. package/node_modules/@m0saic/core/dist/runtime/renderCostModel.js +1 -1
  138. package/node_modules/@m0saic/core/dist/runtime/renderReport.js +1 -1
  139. package/node_modules/@m0saic/core/dist/runtime/renderSidecars.js +1 -1
  140. package/node_modules/@m0saic/core/dist/runtime/resolveMosaicx.js +1 -1
  141. package/node_modules/@m0saic/core/dist/runtime/runPlan.js +1 -1
  142. package/node_modules/@m0saic/core/dist/runtime/types.js +1 -1
  143. package/node_modules/@m0saic/core/dist/runtime/upstream.js +1 -1
  144. package/node_modules/@m0saic/core/dist/runtime/writeSidecars.js +1 -1
  145. package/node_modules/@m0saic/core/dist/types/chunk.js +1 -1
  146. package/node_modules/@m0saic/core/dist/types/index.js +1 -1
  147. package/node_modules/@m0saic/core/dist/types/mosaic-plan.js +1 -1
  148. package/node_modules/@m0saic/core/dist/utils/assertTiming.js +1 -1
  149. package/node_modules/@m0saic/core/dist/utils/command.js +1 -1
  150. package/node_modules/@m0saic/core/dist/utils/createEngineContext.js +1 -1
  151. package/node_modules/@m0saic/core/dist/utils/createTemplateRenderCache.js +1 -1
  152. package/node_modules/@m0saic/core/dist/utils/ffmpegColor.js +1 -1
  153. package/node_modules/@m0saic/core/dist/utils/formatDuration.js +1 -1
  154. package/node_modules/@m0saic/core/dist/utils/fsSafeId.js +1 -1
  155. package/node_modules/@m0saic/core/dist/utils/index.js +1 -1
  156. package/node_modules/@m0saic/core/dist/utils/lowerRenderableToMosaicDocument.js +1 -1
  157. package/node_modules/@m0saic/core/dist/utils/movSafeCodec.js +1 -1
  158. package/node_modules/@m0saic/core/dist/utils/resolveBoxFrac.js +1 -1
  159. package/node_modules/@m0saic/core/dist/validate/index.js +1 -1
  160. package/node_modules/@m0saic/core/scripts/mask-rasterize-core.js +1 -1
  161. package/node_modules/@m0saic/core/scripts/mask-rasterize-worker.js +1 -1
  162. package/node_modules/@m0saic/core/scripts/mask-worker-thread.js +1 -1
  163. package/node_modules/@m0saic/dictionary/NOTICE +9 -0
  164. package/node_modules/@m0saic/dictionary/README.md +315 -0
  165. package/node_modules/@m0saic/dictionary/package.json +1 -1
  166. package/node_modules/@m0saic/dsl/package.json +1 -1
  167. package/node_modules/@m0saic/dsl-file-formats/dist/cjs/agent.js +6 -0
  168. package/node_modules/@m0saic/dsl-file-formats/dist/cjs/m0/parseM0File.js +1 -1
  169. package/node_modules/@m0saic/dsl-file-formats/dist/cjs/m0/serializeM0File.js +2 -2
  170. package/node_modules/@m0saic/dsl-file-formats/dist/cjs/m0c/m0cFile.js +3 -3
  171. package/node_modules/@m0saic/dsl-file-formats/dist/cjs/types.js +1 -1
  172. package/node_modules/@m0saic/dsl-file-formats/package.json +1 -1
  173. package/node_modules/@m0saic/dsl-stdlib/dist/cjs/builders/bakeM0cInsets.js +1 -0
  174. package/node_modules/@m0saic/dsl-stdlib/dist/cjs/builders/index.js +1 -1
  175. package/node_modules/@m0saic/dsl-stdlib/package.json +1 -1
  176. package/node_modules/@m0saic/knowledge/LICENSE +21 -0
  177. package/node_modules/@m0saic/knowledge/README.md +71 -0
  178. package/node_modules/@m0saic/knowledge/dist/index.js +1 -0
  179. package/node_modules/@m0saic/knowledge/docs/README.md +60 -0
  180. package/node_modules/@m0saic/knowledge/docs/file-formats/m0-iteration-protocol.md +120 -0
  181. package/node_modules/@m0saic/knowledge/docs/file-formats/m0p-and-custom-field.md +195 -0
  182. package/node_modules/@m0saic/knowledge/docs/handbook/README.md +27 -0
  183. package/node_modules/@m0saic/knowledge/docs/handbook/composition-arithmetic.md +278 -0
  184. package/node_modules/@m0saic/knowledge/docs/handbook/dsl-complexity.md +75 -0
  185. package/node_modules/@m0saic/knowledge/docs/handbook/dsl-rules.md +367 -0
  186. package/node_modules/@m0saic/knowledge/docs/handbook/feasibility-precision-quantization.md +591 -0
  187. package/node_modules/@m0saic/knowledge/docs/handbook/m0-construction-methods.md +201 -0
  188. package/node_modules/@m0saic/knowledge/docs/handbook/precision-tiers.md +84 -0
  189. package/node_modules/@m0saic/knowledge/docs/m0saic-thesis.md +95 -0
  190. package/node_modules/@m0saic/knowledge/docs/runtime/README.md +24 -0
  191. package/node_modules/@m0saic/knowledge/docs/runtime/agent-at-the-canvas.md +108 -0
  192. package/node_modules/@m0saic/knowledge/docs/runtime/cli-usage.md +475 -0
  193. package/node_modules/@m0saic/knowledge/docs/runtime/ffmpeg-expression-limits.md +117 -0
  194. package/node_modules/@m0saic/knowledge/docs/runtime/reduce-to-one.md +96 -0
  195. package/node_modules/@m0saic/knowledge/docs/skills/README.md +40 -0
  196. package/node_modules/@m0saic/knowledge/docs/skills/axis-and-geometry.md +103 -0
  197. package/node_modules/@m0saic/knowledge/docs/skills/dsl-stdlib-method-catalog.md +7 -0
  198. package/node_modules/@m0saic/knowledge/docs/skills/identity.md +123 -0
  199. package/node_modules/@m0saic/knowledge/docs/skills/labels-and-masks.md +170 -0
  200. package/node_modules/@m0saic/knowledge/docs/skills/m0saic-string-generation.md +251 -0
  201. package/node_modules/@m0saic/knowledge/docs/skills/more-atoms-not-bigger-atoms.md +77 -0
  202. package/node_modules/@m0saic/knowledge/docs/skills/overlay-semantics.md +194 -0
  203. package/node_modules/@m0saic/knowledge/docs/skills/parse-apis.md +79 -0
  204. package/node_modules/@m0saic/knowledge/docs/skills/passthrough-semantics.md +136 -0
  205. package/node_modules/@m0saic/knowledge/docs/skills/structural-construction.md +86 -0
  206. package/node_modules/@m0saic/knowledge/docs/skills/text-in-templates.md +126 -0
  207. package/node_modules/@m0saic/knowledge/docs/skills/zero-overlay-analysis.md +87 -0
  208. package/node_modules/@m0saic/knowledge/docs/templates/README.md +65 -0
  209. package/node_modules/@m0saic/knowledge/docs/templates/capability-templates.md +72 -0
  210. package/node_modules/@m0saic/knowledge/docs/templates/construction-strategy.md +329 -0
  211. package/node_modules/@m0saic/knowledge/docs/templates/data-pipeline.md +324 -0
  212. package/node_modules/@m0saic/knowledge/docs/templates/emission-patterns.md +130 -0
  213. package/node_modules/@m0saic/knowledge/docs/templates/geometry-recipes.md +248 -0
  214. package/node_modules/@m0saic/knowledge/docs/templates/layout-contract.md +168 -0
  215. package/node_modules/@m0saic/knowledge/docs/templates/output-resolution-tree.md +210 -0
  216. package/node_modules/@m0saic/knowledge/docs/templates/patterns/case-study-lessons.md +69 -0
  217. package/node_modules/@m0saic/knowledge/docs/templates/patterns/perf-authoring-rules.md +100 -0
  218. package/node_modules/@m0saic/knowledge/docs/templates/patterns/primitive-extraction-pattern.md +103 -0
  219. package/node_modules/@m0saic/knowledge/docs/templates/philosophy-and-contract.md +310 -0
  220. package/node_modules/@m0saic/knowledge/docs/templates/recursion-nested-rendering.md +154 -0
  221. package/node_modules/@m0saic/knowledge/docs/templates/reference/grid.md +104 -0
  222. package/node_modules/@m0saic/knowledge/docs/templates/reference/json-prop-type.md +169 -0
  223. package/node_modules/@m0saic/knowledge/docs/templates/reference/mosaic-color.md +81 -0
  224. package/node_modules/@m0saic/knowledge/docs/templates/reference/mosaic-placement-props.md +103 -0
  225. package/node_modules/@m0saic/knowledge/docs/templates/reference/prop-bindings.md +338 -0
  226. package/node_modules/@m0saic/knowledge/docs/templates/reference/template-flags.md +236 -0
  227. package/node_modules/@m0saic/knowledge/docs/templates/render-lifecycle.md +117 -0
  228. package/node_modules/@m0saic/knowledge/docs/templates/rendering-model-contract.md +392 -0
  229. package/node_modules/@m0saic/knowledge/docs/templates/standalone-pack-authoring.md +309 -0
  230. package/node_modules/@m0saic/knowledge/docs/templates/theming.md +81 -0
  231. package/node_modules/@m0saic/knowledge/docs/templates/ui-controls.md +150 -0
  232. package/node_modules/@m0saic/knowledge/package.json +6 -0
  233. package/node_modules/@m0saic/momo-types/README.md +109 -0
  234. package/node_modules/@m0saic/momo-types/dist/agent.js +1 -6
  235. package/node_modules/@m0saic/momo-types/dist/context.js +1 -1
  236. package/node_modules/@m0saic/momo-types/package.json +1 -1
  237. package/node_modules/@m0saic/platform/README.md +3 -0
  238. package/node_modules/@m0saic/platform/dist/cjs/index.js +1 -1
  239. package/node_modules/@m0saic/platform/dist/cjs/mosaic/outputKind/outputKind.js +1 -1
  240. package/node_modules/@m0saic/platform/dist/cjs/mosaic/propBindings/index.js +1 -1
  241. package/node_modules/@m0saic/platform/dist/cjs/mosaic/propBindings/propBindings.js +1 -1
  242. package/node_modules/@m0saic/platform/dist/cjs/mosaic/validate/validateSources.js +1 -1
  243. package/node_modules/@m0saic/platform/dist/cjs/packages/mosaicPackages.js +1 -0
  244. package/node_modules/@m0saic/platform/dist/cjs/paths/m0saicRoot.js +1 -1
  245. package/node_modules/@m0saic/platform/dist/cjs/telemetry/lifecycle.js +1 -1
  246. package/node_modules/@m0saic/platform/dist/cjs/telemetry/preview.js +1 -1
  247. package/node_modules/@m0saic/platform/dist/cjs/telemetry/sender.js +1 -1
  248. package/node_modules/@m0saic/platform/dist/cjs/telemetry/usageRecorder.js +1 -1
  249. package/node_modules/@m0saic/platform/dist/cjs/telemetry/usageStore.js +3 -3
  250. package/node_modules/@m0saic/platform/dist/cjs/template-repos/index.js +1 -1
  251. package/node_modules/@m0saic/platform/dist/cjs/template-repos/loadTemplateRepoFromPath.js +4 -4
  252. package/node_modules/@m0saic/platform/dist/cjs/template-repos/templateCatalogSidecars.js +2 -0
  253. package/node_modules/@m0saic/platform/dist/cjs/templateCatalog/index.js +1 -0
  254. package/node_modules/@m0saic/platform/dist/cjs/templateCatalog/templateCatalog.js +1 -0
  255. package/node_modules/@m0saic/platform/dist/cjs/toolchain/ffmpeg/baseline.json +25 -97
  256. package/node_modules/@m0saic/platform/dist/cjs/toolchain/ffmpeg/index.js +1 -1
  257. package/node_modules/@m0saic/platform/dist/cjs/toolchain/ffmpeg/install.js +2 -2
  258. package/node_modules/@m0saic/platform/dist/cjs/toolchain/goldenFfmpeg.js +6 -6
  259. package/node_modules/@m0saic/platform/package.json +1 -1
  260. package/node_modules/@m0saic/product/dist/communityRelease.js +1 -1
  261. package/node_modules/@m0saic/product/dist/freeTierLicense.js +1 -1
  262. package/node_modules/@m0saic/product/dist/index.js +1 -1
  263. package/node_modules/@m0saic/product/dist/licenseActivationNotice.js +1 -0
  264. package/node_modules/@m0saic/product/dist/licenseFile.js +1 -0
  265. package/node_modules/@m0saic/product/dist/licenseToken.js +1 -1
  266. package/node_modules/@m0saic/product/dist/makeScratchPath.js +1 -0
  267. package/node_modules/@m0saic/product/dist/postRenderWrapper.js +1 -1
  268. package/node_modules/@m0saic/product/dist/publicKeys.js +1 -1
  269. package/node_modules/@m0saic/product/dist/revokedKeys.js +1 -1
  270. package/node_modules/@m0saic/product/dist/upstreamDefaults.js +1 -1
  271. package/node_modules/@m0saic/product/dist/workspaceScaffold.js +1 -0
  272. package/node_modules/@m0saic/template-utils/dist/brand/hello-world/helloWorld.js +1 -1
  273. package/node_modules/@m0saic/template-utils/dist/geometry-contract/frameResolution.js +1 -1
  274. package/node_modules/@m0saic/template-utils/dist/geometry-contract/index.js +1 -1
  275. package/node_modules/@m0saic/template-utils/dist/geometry-contract/layoutConstraint.js +2 -2
  276. package/node_modules/@m0saic/template-utils/dist/m0saic/loadSession.js +1 -1
  277. package/node_modules/@m0saic/template-utils/dist/render/createDesignContext.js +1 -1
  278. package/node_modules/@m0saic/template-utils/dist/sources/makeErrorMosaic.js +2 -2
  279. package/node_modules/@m0saic/template-utils/dist/sources/svgTextSource.js +1 -1
  280. package/node_modules/@m0saic/template-utils/dist/template/auditRenderedTemplate.js +5 -5
  281. package/node_modules/@m0saic/template-utils/dist/template/auditSchemaConventions.js +1 -1
  282. package/node_modules/@m0saic/template-utils/dist/template/defineMosaicTemplate.js +1 -1
  283. package/node_modules/@m0saic/template-utils/dist/template/resolveTemplateOutputHints.js +1 -1
  284. package/node_modules/@m0saic/template-utils/dist/template/templateCatalogStore.js +1 -0
  285. package/node_modules/@m0saic/template-utils/dist/template/templateConventions.js +4 -4
  286. package/node_modules/@m0saic/template-utils/dist/template/templateRegistry.js +3 -3
  287. package/node_modules/@m0saic/template-utils/dist/template/validateTemplateProps.js +1 -1
  288. package/node_modules/@m0saic/template-utils/dist/text/fitText.js +6 -6
  289. package/node_modules/@m0saic/template-utils/package.json +1 -1
  290. package/node_modules/@m0saic/templates/NOTICE +9 -0
  291. package/node_modules/@m0saic/templates/dist/catalog.js +1 -0
  292. package/node_modules/@m0saic/templates/dist/freeze.js +8 -8
  293. package/node_modules/@m0saic/templates/dist/index.js +1 -1
  294. package/node_modules/@m0saic/templates/dist/m0saic/_shared/canvas-handles.js +1 -0
  295. package/node_modules/@m0saic/templates/dist/m0saic/_shared/stand-in.js +1 -0
  296. package/node_modules/@m0saic/templates/dist/m0saic/agents/commit-feed/index.js +1 -1
  297. package/node_modules/@m0saic/templates/dist/m0saic/agents/commit-feed/v2/commit-feed.js +1 -0
  298. package/node_modules/@m0saic/templates/dist/m0saic/agents/commit-feed/v2/index.js +1 -0
  299. package/node_modules/@m0saic/templates/dist/m0saic/agents/trace-timeline/index.js +1 -1
  300. package/node_modules/@m0saic/templates/dist/m0saic/agents/trace-timeline/v2/index.js +1 -0
  301. package/node_modules/@m0saic/templates/dist/m0saic/agents/trace-timeline/v2/trace-timeline.js +1 -0
  302. package/node_modules/@m0saic/templates/dist/m0saic/alpine/_shared/alpine-conventions.js +1 -0
  303. package/node_modules/@m0saic/templates/dist/m0saic/alpine/bar-graph/v2/bar-graph.js +1 -0
  304. package/node_modules/@m0saic/templates/dist/m0saic/alpine/bar-graph/v2/index.js +1 -0
  305. package/node_modules/@m0saic/templates/dist/m0saic/alpine/commit-feed/v3/commit-feed.js +1 -0
  306. package/node_modules/@m0saic/templates/dist/m0saic/alpine/commit-feed/v3/index.js +1 -0
  307. package/node_modules/@m0saic/templates/dist/m0saic/alpine/contributor-table/v2/contributor-table.js +1 -0
  308. package/node_modules/@m0saic/templates/dist/m0saic/alpine/contributor-table/v2/index.js +1 -0
  309. package/node_modules/@m0saic/templates/dist/m0saic/alpine/donut/v4/donut.js +1 -0
  310. package/node_modules/@m0saic/templates/dist/m0saic/alpine/donut/v4/index.js +1 -0
  311. package/node_modules/@m0saic/templates/dist/m0saic/alpine/heatmap/v3/heatmap.js +1 -0
  312. package/node_modules/@m0saic/templates/dist/m0saic/alpine/heatmap/v3/index.js +1 -0
  313. package/node_modules/@m0saic/templates/dist/m0saic/alpine/index.js +1 -1
  314. package/node_modules/@m0saic/templates/dist/m0saic/alpine/kpi-card/v3/index.js +1 -0
  315. package/node_modules/@m0saic/templates/dist/m0saic/alpine/kpi-card/v3/kpi-card.js +1 -0
  316. package/node_modules/@m0saic/templates/dist/m0saic/alpine/leaderboard/v2/index.js +1 -0
  317. package/node_modules/@m0saic/templates/dist/m0saic/alpine/leaderboard/v2/leaderboard.js +1 -0
  318. package/node_modules/@m0saic/templates/dist/m0saic/alpine/line-chart/v3/index.js +1 -0
  319. package/node_modules/@m0saic/templates/dist/m0saic/alpine/line-chart/v3/line-chart.js +1 -0
  320. package/node_modules/@m0saic/templates/dist/m0saic/alpine/progress-card/v2/index.js +1 -0
  321. package/node_modules/@m0saic/templates/dist/m0saic/alpine/progress-card/v2/progress-card.js +1 -0
  322. package/node_modules/@m0saic/templates/dist/m0saic/alpine/stat-card/v2/index.js +1 -0
  323. package/node_modules/@m0saic/templates/dist/m0saic/alpine/stat-card/v2/stat-card.js +1 -0
  324. package/node_modules/@m0saic/templates/dist/m0saic/alpine/timeline/v3/index.js +1 -0
  325. package/node_modules/@m0saic/templates/dist/m0saic/alpine/timeline/v3/timeline.js +4 -0
  326. package/node_modules/@m0saic/templates/dist/m0saic/alpine/treemap/v3/index.js +1 -0
  327. package/node_modules/@m0saic/templates/dist/m0saic/alpine/treemap/v3/treemap.js +1 -0
  328. package/node_modules/@m0saic/templates/dist/m0saic/benchmark/index.js +1 -1
  329. package/node_modules/@m0saic/templates/dist/m0saic/benchmark/report/v2/index.js +1 -0
  330. package/node_modules/@m0saic/templates/dist/m0saic/benchmark/report/v2/report.js +1 -0
  331. package/node_modules/@m0saic/templates/dist/m0saic/brand/business-card/v2/business-card.js +1 -0
  332. package/node_modules/@m0saic/templates/dist/m0saic/brand/business-card/v2/index.js +1 -0
  333. package/node_modules/@m0saic/templates/dist/m0saic/brand/index.js +1 -1
  334. package/node_modules/@m0saic/templates/dist/m0saic/brand/logo/v4/index.js +1 -0
  335. package/node_modules/@m0saic/templates/dist/m0saic/brand/logo/v4/logo_runner.js +1 -0
  336. package/node_modules/@m0saic/templates/dist/m0saic/charts/bar-graph/v3/bar-graph.js +1 -0
  337. package/node_modules/@m0saic/templates/dist/m0saic/charts/bar-graph/v3/index.js +1 -0
  338. package/node_modules/@m0saic/templates/dist/m0saic/charts/donut/v5/donut.js +1 -0
  339. package/node_modules/@m0saic/templates/dist/m0saic/charts/donut/v5/index.js +1 -0
  340. package/node_modules/@m0saic/templates/dist/m0saic/charts/index.js +1 -1
  341. package/node_modules/@m0saic/templates/dist/m0saic/charts/line-chart/v2/index.js +1 -0
  342. package/node_modules/@m0saic/templates/dist/m0saic/charts/line-chart/v2/line-chart.js +1 -0
  343. package/node_modules/@m0saic/templates/dist/m0saic/charts/stat-card/v2/index.js +1 -0
  344. package/node_modules/@m0saic/templates/dist/m0saic/charts/stat-card/v2/stat-card.js +1 -0
  345. package/node_modules/@m0saic/templates/dist/m0saic/code/index.js +1 -1
  346. package/node_modules/@m0saic/templates/dist/m0saic/code/snippet-morph/v2/snippet-morph.js +3 -0
  347. package/node_modules/@m0saic/templates/dist/m0saic/collage/image-collage/v2/image-collage.js +1 -0
  348. package/node_modules/@m0saic/templates/dist/m0saic/collage/image-collage/v2/index.js +1 -0
  349. package/node_modules/@m0saic/templates/dist/m0saic/collage/index.js +1 -1
  350. package/node_modules/@m0saic/templates/dist/m0saic/dsl-tutorial/index.js +1 -1
  351. package/node_modules/@m0saic/templates/dist/m0saic/dsl-tutorial/v2/dsl-tutorial.js +1 -0
  352. package/node_modules/@m0saic/templates/dist/m0saic/dsl-tutorial/v2/index.js +1 -0
  353. package/node_modules/@m0saic/templates/dist/m0saic/github/year-card/index.js +1 -1
  354. package/node_modules/@m0saic/templates/dist/m0saic/github/year-card/v2/index.js +1 -0
  355. package/node_modules/@m0saic/templates/dist/m0saic/github/year-card/v2/year-card.js +1 -0
  356. package/node_modules/@m0saic/templates/dist/m0saic/hero/ffmpeg-pulse/index.js +1 -1
  357. package/node_modules/@m0saic/templates/dist/m0saic/hero/ffmpeg-pulse/runner/v2/index.js +1 -0
  358. package/node_modules/@m0saic/templates/dist/m0saic/hero/ffmpeg-pulse/runner/v2/runner.js +1 -0
  359. package/node_modules/@m0saic/templates/dist/m0saic/media/barcode/v2/barcode.js +1 -0
  360. package/node_modules/@m0saic/templates/dist/m0saic/media/barcode/v2/index.js +1 -0
  361. package/node_modules/@m0saic/templates/dist/m0saic/media/blur-regions/index.js +1 -1
  362. package/node_modules/@m0saic/templates/dist/m0saic/media/blur-regions/v1/tutorial.js +1 -1
  363. package/node_modules/@m0saic/templates/dist/m0saic/media/blur-regions/v2/blur-regions.js +1 -0
  364. package/node_modules/@m0saic/templates/dist/m0saic/media/blur-regions/v2/index.js +1 -0
  365. package/node_modules/@m0saic/templates/dist/m0saic/media/highlights/index.js +1 -1
  366. package/node_modules/@m0saic/templates/dist/m0saic/media/highlights/v2/highlights.js +1 -0
  367. package/node_modules/@m0saic/templates/dist/m0saic/media/highlights/v2/index.js +1 -0
  368. package/node_modules/@m0saic/templates/dist/m0saic/media/index.js +1 -1
  369. package/node_modules/@m0saic/templates/dist/m0saic/media/logo-animate/index.js +1 -1
  370. package/node_modules/@m0saic/templates/dist/m0saic/media/logo-animate/v2/index.js +1 -0
  371. package/node_modules/@m0saic/templates/dist/m0saic/media/logo-animate/v2/logo-animate.js +1 -0
  372. package/node_modules/@m0saic/templates/dist/m0saic/media/metadata-stamp/index.js +1 -1
  373. package/node_modules/@m0saic/templates/dist/m0saic/media/metadata-stamp/v2/index.js +1 -0
  374. package/node_modules/@m0saic/templates/dist/m0saic/media/metadata-stamp/v2/metadata-stamp.js +1 -0
  375. package/node_modules/@m0saic/templates/dist/m0saic/media/qr/code/v2/index.js +1 -0
  376. package/node_modules/@m0saic/templates/dist/m0saic/media/qr/code/v2/qr-code.js +1 -0
  377. package/node_modules/@m0saic/templates/dist/m0saic/media/qr/index.js +1 -1
  378. package/node_modules/@m0saic/templates/dist/m0saic/media/qr/stamp/v2/qr-stamp-custom.js +1 -0
  379. package/node_modules/@m0saic/templates/dist/m0saic/media/screencap_grid/index.js +1 -1
  380. package/node_modules/@m0saic/templates/dist/m0saic/media/screencap_grid/v3/index.js +1 -0
  381. package/node_modules/@m0saic/templates/dist/m0saic/media/screencap_grid/v3/screencap-grid.js +4 -0
  382. package/node_modules/@m0saic/templates/dist/m0saic/media/screencap_grid_aspect_safe/index.js +1 -1
  383. package/node_modules/@m0saic/templates/dist/m0saic/media/screencap_grid_aspect_safe/v2/index.js +1 -0
  384. package/node_modules/@m0saic/templates/dist/m0saic/media/screencap_grid_aspect_safe/v2/screencap-grid-aspect-safe.js +3 -0
  385. package/node_modules/@m0saic/templates/dist/m0saic/media/scroll_wall/index.js +1 -1
  386. package/node_modules/@m0saic/templates/dist/m0saic/media/scroll_wall/v2/index.js +1 -0
  387. package/node_modules/@m0saic/templates/dist/m0saic/media/scroll_wall/v2/scroll-wall.js +1 -0
  388. package/node_modules/@m0saic/templates/dist/m0saic/media/subtitle-burn/index.js +1 -1
  389. package/node_modules/@m0saic/templates/dist/m0saic/media/subtitle-burn/v2/index.js +1 -0
  390. package/node_modules/@m0saic/templates/dist/m0saic/media/subtitle-burn/v2/subtitle-burn.js +4 -0
  391. package/node_modules/@m0saic/templates/dist/m0saic/media/trickplay/index.js +1 -1
  392. package/node_modules/@m0saic/templates/dist/m0saic/media/trickplay/v2/index.js +1 -0
  393. package/node_modules/@m0saic/templates/dist/m0saic/media/trickplay/v2/trickplay.js +1 -0
  394. package/node_modules/@m0saic/templates/dist/m0saic/media/video_to_png_sequence/index.js +1 -1
  395. package/node_modules/@m0saic/templates/dist/m0saic/media/video_to_png_sequence/v2/index.js +1 -0
  396. package/node_modules/@m0saic/templates/dist/m0saic/media/video_to_png_sequence/v2/video-to-png-sequence.js +1 -0
  397. package/node_modules/@m0saic/templates/dist/m0saic/media/watermark/index.js +1 -1
  398. package/node_modules/@m0saic/templates/dist/m0saic/media/watermark/v2/index.js +1 -0
  399. package/node_modules/@m0saic/templates/dist/m0saic/media/watermark/v2/watermark.js +1 -0
  400. package/node_modules/@m0saic/templates/dist/m0saic/primitives/grid/index.js +1 -1
  401. package/node_modules/@m0saic/templates/dist/m0saic/primitives/grid/v3/grid.js +1 -0
  402. package/node_modules/@m0saic/templates/dist/m0saic/social/quote-card/index.js +1 -1
  403. package/node_modules/@m0saic/templates/dist/m0saic/social/quote-card/v2/index.js +1 -0
  404. package/node_modules/@m0saic/templates/dist/m0saic/social/quote-card/v2/quote-card.js +1 -0
  405. package/node_modules/@m0saic/templates/dist/m0saic/story/index.js +1 -1
  406. package/node_modules/@m0saic/templates/dist/m0saic/story/narrated-chapters/index.js +1 -1
  407. package/node_modules/@m0saic/templates/dist/m0saic/story/narrated-chapters/v2/index.js +1 -0
  408. package/node_modules/@m0saic/templates/dist/m0saic/story/narrated-chapters/v2/narrated-chapters.js +4 -0
  409. package/node_modules/@m0saic/templates/dist/m0saic/story/scrapbook/v2/index.js +1 -0
  410. package/node_modules/@m0saic/templates/dist/m0saic/story/scrapbook/v2/props.js +1 -0
  411. package/node_modules/@m0saic/templates/dist/m0saic/story/scrapbook/v2/scrapbook.js +1 -0
  412. package/node_modules/@m0saic/templates/dist/m0saic/theming/index.js +1 -1
  413. package/node_modules/@m0saic/templates/dist/m0saic/theming/v2/index.js +1 -0
  414. package/node_modules/@m0saic/templates/dist/m0saic/theming/v2/theming.js +1 -0
  415. package/node_modules/@m0saic/templates/dist/m0saic/web/index.js +1 -1
  416. package/node_modules/@m0saic/templates/dist/m0saic/web/page-skeleton/v2/index.js +1 -0
  417. package/node_modules/@m0saic/templates/dist/m0saic/web/page-skeleton/v2/page-skeleton.js +1 -0
  418. package/node_modules/@m0saic/templates/dist/m0saic/wireframe/base/v3/index.js +1 -0
  419. package/node_modules/@m0saic/templates/dist/m0saic/wireframe/base/v3/wireframe.js +1 -0
  420. package/node_modules/@m0saic/templates/dist/m0saic/wireframe/index.js +1 -1
  421. package/node_modules/@m0saic/templates/dist/repo.js +1 -1
  422. package/node_modules/@m0saic/templates/dist/template-catalog.json +5429 -0
  423. package/node_modules/@m0saic/templates/dist/template-registry.js +1 -1
  424. package/node_modules/@m0saic/templates/dist/web.js +1 -1
  425. package/node_modules/@m0saic/templates/package.json +1 -1
  426. package/node_modules/@m0saic/templates/template-catalog.json +5429 -0
  427. package/node_modules/@m0saic/templates/template-manifest.json +1030 -286
  428. package/node_modules/@m0saic/templates/web-template-ids.json +38 -0
  429. package/node_modules/@m0saic/templates-advanced/NOTICE +9 -0
  430. package/node_modules/@m0saic/text/package.json +1 -1
  431. package/node_modules/@m0saic/types/dist/analytics/consent.js +1 -1
  432. package/node_modules/@m0saic/types/dist/analytics/event.js +1 -1
  433. package/node_modules/@m0saic/types/dist/analytics/usageMetrics.js +1 -1
  434. package/node_modules/@m0saic/types/dist/identifiers/identifiers.js +1 -1
  435. package/node_modules/@m0saic/types/dist/telemetry/ffmpegFailure.js +1 -1
  436. package/node_modules/@m0saic/types/dist/template/template.js +1 -1
  437. package/node_modules/@m0saic/types/package.json +1 -1
  438. package/node_modules/@m0saic/types-internal/dist/analytics/featureKeys.js +1 -1
  439. package/package.json +13 -11
  440. package/resources/starter-base/starter-base.json +10 -0
  441. package/resources/starter-base/starter-base.tgz +0 -0
@@ -0,0 +1,338 @@
1
+ # Prop bindings — the rect that SHOWS a prop is its handle in Make
2
+
3
+ A template marks the source that DISPLAYS a prop with `source.editor.binding`
4
+ (or `bindings`, several leaves on one rect). Make resolves them per render and
5
+ turns the rect into the prop's canvas handle: double-click / a badge to edit,
6
+ a file dropped on it to fill a media slot. Bindings are re-resolved on every
7
+ render and keyed by the PROP — never by a stableKey, which changes whenever the
8
+ geometry does.
9
+
10
+ One predicate decides what is bindable: `classifyBindableProp` in
11
+ `@m0saic/platform` (`mosaic/propBindings`). Template tests
12
+ (`resolvePropBindings` in `@m0saic/template-utils`) and the Make page both use
13
+ it, so a template can never offer an edit the page refuses, or vice versa.
14
+ `bindProps` / `bindPropPath` THROW at author time for what the platform would
15
+ silently drop — a template test should never reach the silent path.
16
+
17
+ ## The wire shape
18
+
19
+ ```ts
20
+ binding: {
21
+ propKey: string; // dotted path as propsSchema nests it: "label", "titles.title"
22
+ index?: number; // ONE element of string[] / number[] / media[]
23
+ path?: Array<string | number>; // route into a json / list / array value: [2, "title"]
24
+ kind?: "string" | "number" | "color" | "rect" | "media"; // required on structured leaves
25
+ layer?: number; // which text layer shows the leaf (multi-layer text)
26
+ onClear?: "remove-element" | "unset-leaf";
27
+ seedDraft?: string;
28
+ companion?: boolean;
29
+ range?: { start: number; end: number }; // one line of a multi-line string leaf
30
+ focus?: { start: number; end: number }; // the token the rect shows, inside range
31
+ }
32
+ ```
33
+
34
+ Rules:
35
+
36
+ - `propKey` is exactly as `propsSchema` nests it; every segment past the
37
+ first walks a `type: "group"` prop's `fields`.
38
+ - Basic lists (`string[]`, `number[]`, `media[]`) bind ONE element — a
39
+ non-negative integer `index`. The list itself is never bindable.
40
+ - Structured props (`json` / `list` / `array`) bind a LEAF: `path` from the
41
+ prop's value plus an explicit `kind` (the schema has no per-leaf type, and an
42
+ empty leaf must still be an "add" handle). `index` is shorthand for a leading
43
+ numeric segment.
44
+ - `meta.ui.hidden` props, booleans, connection-driven option strings
45
+ (`optionsFromConnection` — the host has no list without the connection) and
46
+ whole lists are never bindable. A stray `index` on a scalar is ignored.
47
+ - A **closed set** — `constraints.oneOf` or a static `control.options` list — binds
48
+ as kind **`choice`** (2026-10-01): Make shows the set (a segmented row up to four
49
+ choices, a select beyond) on the inline editor and on the tile card, and one click
50
+ writes the value; nothing is typed, `seedDraft` is dropped. Basic props only — a
51
+ structured leaf has no set to offer, so `kind: "choice"` there is refused. The case it exists for:
52
+ a full-canvas card bound to its theme `preset` (the alpine cards, the GitHub year
53
+ card) — the click on the card is where the theme gets changed, and the `canvasFill`
54
+ rule accepts a full-canvas rect that carries such a handle.
55
+ - Bind even when the value is empty — the rect is then a handle to ADD.
56
+
57
+ ## Kinds
58
+
59
+ | prop / leaf | binding | kind | Make handle |
60
+ |---|---|---|---|
61
+ | free-text `string` (or one `string[]` element, or a structured leaf) | `bindProp(src, key[, i])` / `bindPropPath(src, key, path, "string")` | `"string"` | T — the inline text form |
62
+ | colour-valued string (`isColor` / `colorPicker`) | same | `"color"` | swatch — a picker |
63
+ | `number` (or one `number[]` element, or a leaf) | same, `"number"` | `"number"` | 123 — the inline form |
64
+ | `json` with `picker: "regions"`, `regions.max: 1` | `bindPropRect(src, key)` — no path | `"rect"` | move glyph — the regions draw session on the cell |
65
+ | `media`, one `media[]` element, or a leaf holding a path | `bindProp(src, key[, i])` / `bindPropPath(…, "media")` | `"media"` | picture glyph — a DROP target + the media chip |
66
+
67
+ Order the entries the way a double-click should read them: `fields[0]` is the
68
+ primary action (a plain double-click); the badge row offers every kind.
69
+
70
+ ## The roll call — `bindingsDeclared` (THROW, 2026-09-25)
71
+
72
+ **Every prop that CAN carry a canvas handle is either bound, or declared.** A new template that does
73
+ neither fails the build. Founder ruling: "if it makes sense to bind, we do — or warn if it's not."
74
+
75
+ ```ts
76
+ bindings: { unbound: { fps: "timing", gap: "geometry", seed: "determinism" } }
77
+ ```
78
+
79
+ One honest word is the point — the reason is for a reviewer. Keys are dotted exactly as `propsSchema`
80
+ nests them (`titles.title`).
81
+
82
+ ### The complete surface — one ruling per control
83
+
84
+ `MUST ACCOUNT` = bind it, or name it in `bindings.unbound`. `NEVER` = not a canvas thing; the
85
+ right-hand panel is its surface, and the rule stays silent.
86
+
87
+ | Prop / flavour | Handle | Convention |
88
+ |---|---|---|
89
+ | `string`, free text | `string` | **MUST ACCOUNT** |
90
+ | `string` + `isColor` / `colorPicker` | `color` | **MUST ACCOUNT** |
91
+ | `number` | `number` | **MUST ACCOUNT** |
92
+ | `media` | `media` (also a drop target) | **MUST ACCOUNT** |
93
+ | `string[]` · `number[]` · `media[]` | per element, by index | **MUST ACCOUNT** (bound anywhere on the prop counts) |
94
+ | `json` · `list` · `array` | per leaf, `path` + `kind` | **MUST ACCOUNT** (any leaf counts) |
95
+ | `json` + `picker: "regions"` | `rect` | **MUST ACCOUNT** |
96
+ | `string` + closed set (`oneOf`, a static `options` list) | `choice` (a pick, never typed) | **NEVER DEMANDED** — a mode has no rect of its own; bind it when a rect IS the mode (the card → `preset`) |
97
+ | `string` / `number` + `optionsFromConnection` | — | **NEVER** — the host has no list without the connection |
98
+ | `number` + closed set | — | **NEVER** |
99
+ | `boolean` | — | **NEVER** — a one-way door: `false` renders no rect to click |
100
+ | `group` | — | **NEVER** — a container; its fields are walked individually |
101
+ | `m0` · `m0c` · `m0p` | — | **NEVER** — the value IS the composition of every rect, not one of them |
102
+ | `code` | — | **NEVER** — read-only by contract; a handle implies editing |
103
+ | anything `ui.hidden` or `consumer: "human"` | — | **NEVER** |
104
+
105
+ Why the `m0` family is excluded, since it is the tempting one: a layout prop has no clickable element.
106
+ All 10 shipped layout props (wireframe, dsl-tutorial, screencap-grid v1/v2, watermark, qr-animate,
107
+ camera-debug) drive the ROOT composition — you would have to select the whole grid, which the canvas
108
+ cannot express. If a template ever nests a layout prop into ONE cell, that cell is a real handle and a
109
+ sixth kind (`"layout"`) becomes worth having. None does today.
110
+
111
+ **Bindable ≠ has a rect.** The predicate says a `number` is always inline-editable; whether any rect
112
+ *shows* it is the template's business. `durationMs`, `fps`, `gap`, `seed`, `padding` are all
113
+ accountable and correctly unbound — which is exactly why the rule takes a declaration instead of
114
+ guessing from the pixels.
115
+
116
+ **A colour that IS `document.backgroundColor` needs nothing** — no binding, no declaration. It has no
117
+ source and no rect, so no handle can exist, and it is the way a canvas SHOULD be filled: a full-frame
118
+ base rect becomes a click target that shadows everything behind it whenever the pointer is not on a
119
+ smaller tile (founder direction 2026-09-25). Matched case-insensitively, root or child.
120
+
121
+ **A binding in a nested CHILD counts.** A template that composes its content into a child document
122
+ (the hello-world card puts everything in a `card` child) reaches its props perfectly well — Make
123
+ resolves a binding through children. **But the audit renders at `defaultProps`,** so a rect the
124
+ template only creates when a prop is non-empty reads as unbound. That is the "bind even when the value
125
+ is empty" rule biting: bind it unconditionally, or declare it.
126
+
127
+ **A stale declaration is a violation too:** naming a prop that cannot carry a handle, naming one that
128
+ is in fact bound, or leaving the reason empty. An entry claims a reviewer looked at that prop.
129
+
130
+ **Exempt:** any template hashed in the repo's `frozen.manifest.json`. It shipped before the
131
+ convention, it is never edited in place, and the fix would be a vN+1 nobody will write. A repo with no
132
+ manifest has shipped nothing and holds every template to the rule.
133
+
134
+ ## The in-context line — `bindingHints` (THROW, 2026-09-27)
135
+
136
+ **Every bound rect shows one line in context.** Make puts it under the inline editor when a person
137
+ double-clicks, on the tile card's row, and in the handle's hover tooltip — what changing the value
138
+ does and what it looks like, so nobody has to open the settings pane or a manual to understand a knob.
139
+ Founder ruling: a production-grade template adheres to all the surfaces of the ecosystem, and the
140
+ canvas is now the primary one; "it's better to take this tax now."
141
+
142
+ The line comes from either of two places, and the gate accepts either:
143
+
144
+ | Where | How | Reaches |
145
+ |---|---|---|
146
+ | the prop | the catalog sidecar: `props.<key>.description` in `<name>.catalog.json` (0.3.1; `propsSchema.<key>.description` in code is the pre-0.3.1 form, kept only by shipped templates) | every rect bound to that prop |
147
+ | the binding | `bindProp(src, key, i, { hint: "…" })` · a `bindProps` entry's `hint` · `withBindingHint(src, "…")` (composes with every binder) | that rect only — the wording for THIS place |
148
+
149
+ One plain sentence, never markdown, never the value itself ("The month this calendar shows; the grid
150
+ re-flows to its weeks", not "October"). Make trims it and caps it near 200 characters. The tax is
151
+ therefore one honest sentence per bound prop, paid once in the sidecar; a per-rect `hint` (code — it
152
+ describes one specific rect) is for the cases where the same prop means something different in
153
+ different places. The gate judges hints AFTER the catalog applies, so a sidecar description satisfies it.
154
+
155
+ **Needs none:** a `companion` leaf (filled only by a media drop, never shown in a form), and any prop
156
+ that is not bound (an `unbound` declaration already carries its reason). A binding on an unknown prop
157
+ is `bindingsSound`'s finding, not this one.
158
+
159
+ **Exempt:** any template hashed in the repo's `frozen.manifest.json`, like the roll call — it reports
160
+ in `lagging`, never `findings`. The CLI ignores the field entirely; it is extra data there.
161
+
162
+ ## The `bindingsCover` convention — what the gate checks
163
+
164
+ The roll call above is the gate. `bindingsCover` is the WEAKER SECOND SIGNAL kept beside it
165
+ (posture `record` → a warning): it reads the drawn text, so it is the only rule that can catch a prop
166
+ bound to the WRONG rect, and it still fires on frozen templates where the roll call cannot. Both
167
+ `check-registry.mjs` copies and `m0saic doctor <repo>` run both.
168
+
169
+ - A **free-text `string`** prop — not `meta.ui.hidden`, not `consumer: "human"`, not a closed /
170
+ picked set — whose default is ≥2 characters and appears verbatim in drawn text must be bound.
171
+ - A **`number`** prop, same exclusions, whose default appears in drawn text in one of its honest
172
+ spellings: `String(v)`, `toLocaleString("en-US")`, and `toFixed(1|2)` **only when the value already
173
+ has decimals** (nothing draws an integer count as `"1.0"`, but chrome draws versions that read that
174
+ way). A one-character spelling never counts.
175
+ - Numbers match on a **digit boundary**: `12` is not read out of `2012` or `3.12`, and a drawn
176
+ `1,200` is not read as the prop `200`.
177
+ - Coverage is per PROP, not per rect — a prop bound anywhere on the root document is covered.
178
+
179
+ ### One rect, two props — a composite line
180
+
181
+ `"@qsbuilds · 2026 on GitHub"` is one rect drawing two props, and a single `editor.binding` names one
182
+ of them. A bare `bindProp` there would REPLACE the existing handle, so the rule says so and names the
183
+ fix that keeps both:
184
+
185
+ ```ts
186
+ bindProps(src, [{ propKey: "handle" }, { propKey: "year", kind: "number" }])
187
+ ```
188
+
189
+ — several handles on one rect — or split the line so each prop gets its own rect. (`year-card/v1` is
190
+ the live example; it is frozen, so its fix is v2 and it warns until then.)
191
+
192
+ Colour coverage is not checked: a colour is never drawn as text, so it needs a different detector.
193
+
194
+ ## Authoring helpers (`@m0saic/template-utils`)
195
+
196
+ - `bindProp(src, propKey, index?)` — the basic case.
197
+ - `bindProps(src, entries)` — several leaves on one rect (a multi-layer text,
198
+ a cell that moves AND edits AND takes a drop). Writes `editor.bindings`,
199
+ which wins over the single `binding`.
200
+ - `bindPropPath(src, propKey, path, kind, { onClear?, seedDraft? })` — one
201
+ structured leaf.
202
+ - `bindPropRect(src, propKey)` — a one-region regions picker edited in place.
203
+ - `bindPropRange(src, propKey, index, range, focus?)` — one line of a
204
+ multi-line string prop (a code state, a lyric block); offsets index the RAW
205
+ prop string.
206
+ - `stripPropBindings(doc)` — a host composing ANOTHER template's render drops
207
+ its bindings (they name the inner template's props).
208
+ - `tag(src, label)` composes either way round with all of them.
209
+
210
+ ## `onClear` — what an EMPTY commit means
211
+
212
+ ```ts
213
+ { propKey: "days", path: [rowIndex, "day"], kind: "number", onClear: "remove-element" }
214
+ ```
215
+
216
+ - `"remove-element"` — splice the element at the leading numeric path segment
217
+ (the whole row, or one element of a basic list) out of its array. Needs an
218
+ `index` or a numeric first `path` segment.
219
+ - `"unset-leaf"` — delete the leaf key from its object, or unset a basic prop
220
+ so its default shows again. Not for array-element leaves (no key).
221
+ - Absent — an OPTIONAL colour prop (a basic prop or a group field, not a leaf
222
+ inside a row or list) unsets, back to the template's own colour (platform
223
+ `defaultOnClear`); a required colour and other numbers refuse an empty commit;
224
+ strings write `""`; a scalar media clears to `""`; a `media[]` element refuses
225
+ (a hole in a media list is not a value the engine can open). `"none"` is never
226
+ a clear — it is a colour (transparent).
227
+
228
+ Editor rules: "empty" is blank for numbers / colours, `""` for text. An empty
229
+ commit against an already-empty leaf writes nothing. In a multi-field session a
230
+ row removal supersedes its siblings' writes; rows go highest index first. The
231
+ panel stays the primary place rows are deleted — canvas clear is an accelerator.
232
+
233
+ ## `seedDraft` — prefill an EMPTY leaf from the rect's context
234
+
235
+ ```ts
236
+ { propKey: "days", path: [len, "day"], kind: "number", onClear: "remove-element", seedDraft: String(info.day) }
237
+ ```
238
+
239
+ - Applies only when the bound leaf reads EMPTY / absent; a leaf with a value
240
+ ignores it. A DRAFT: shown pre-selected (Enter alone accepts it), written on
241
+ commit like typed text, never applied by itself. Deleting it and committing
242
+ empty still runs `onClear`.
243
+ - Blur commits too, so an untouched seeded form that loses focus WRITES the
244
+ seed — mind this when seeding.
245
+ - Wire shape is a string; a blank seed, or a non-numeric seed on a number
246
+ leaf, throws at author time; `rect` and `media` leaves take no seed.
247
+ - Focus lands on the FIRST field: order a seeded field first when the seed is
248
+ the whole suggestion.
249
+
250
+ ## `kind: "media"` — a rect that takes a dropped file
251
+
252
+ A media-bound rect is a DROP target on the Make canvas (a file dragged onto it
253
+ writes the path) with a picture-glyph handle that opens the media chip —
254
+ `Choose file…` / `Replace…` · `Remove` (when clearable) · "or drop a file on
255
+ the cell". A file in flight lights every media slot; a slot whose prop refuses
256
+ the kind lights muted.
257
+
258
+ - What may land is the PROP's say: `meta.control.accept` (`["image","video"]`)
259
+ and `meta.control.extensions`. Judged by MIME while in flight, by extension
260
+ at drop.
261
+ - `media[]`: index `< length` replaces, `=== length` appends (an add handle),
262
+ `> length` is refused. A media list is never padded with `""`.
263
+ - A tile pairs handles freely — the drop-calendar's facecam is
264
+ `bindProps(src, [{ propKey: "facecamRegion", kind: "rect" }, { propKey: "facecam" }])`:
265
+ move it, or drop on it. Put the MEDIA entry first when a double-click should
266
+ mean "replace the picture".
267
+
268
+ ### A drop commits the tile's seeded companions
269
+
270
+ When a file lands, every EMPTY sibling leaf on the tile that carries a
271
+ `seedDraft` is written with its seed — the rect's meaning travels with the
272
+ gesture (the clicked date; the slot number). Leaves with a value, or empty
273
+ leaves without a seed, are untouched.
274
+
275
+ `companion: true` marks a seeded leaf ONLY the drop fills:
276
+
277
+ ```ts
278
+ { propKey: "days", path: [row, "teaser"], kind: "number", seedDraft: String(teasers.length + 1), companion: true }
279
+ ```
280
+
281
+ It never shows in the text form (a blur-commit there would write the seed on
282
+ an unrelated edit) and never earns a badge. Needs a kept `seedDraft`; refused
283
+ on media / rect leaves. The drop-calendar date cell, in full — one drop births
284
+ a pictured row:
285
+
286
+ ```ts
287
+ bindProps(cell, [
288
+ { propKey: "days", path: [n, "day"], kind: "number", onClear: "remove-element", seedDraft: String(day) },
289
+ { propKey: "days", path: [n, "title"], kind: "string" },
290
+ { propKey: "teasers", index: teasers.length, kind: "media" },
291
+ { propKey: "days", path: [n, "teaser"], kind: "number", seedDraft: String(teasers.length + 1), companion: true },
292
+ ])
293
+ ```
294
+
295
+ ## Starter media — the slot before the footage
296
+
297
+ A template that wants the slot present (and droppable) before the user has a
298
+ file renders a stand-in while the prop is empty. Two sources:
299
+
300
+ 1. **Bring your own** — media bundled next to the template
301
+ (`assets/*.png|mp4|svg`, mirrored by copy-assets), built with
302
+ `fileAsset(resolve(__dirname, "assets"), name, kind)` from
303
+ `@m0saic/template-utils/dist/m0saic/assetPath` (asar-safe).
304
+ 2. **Included defaults** — `starterMedia(role)` from
305
+ `@m0saic/template-utils/dist/media/defaultMedia` (node-only deep import):
306
+ roles `portrait` `landscape` `square` `facecam` `avatar` `logo`, one small
307
+ viewBox-only SVG each (`assets/starter/`, minted by
308
+ `tools/mint-starter-svg.mjs`). Returns `null` when the file is not on disk —
309
+ degrade to the template's own stand-in, never an error mosaic.
310
+
311
+ An `.svg` file asset is rasterized to a PNG at plan time by the engine, and
312
+ Make's preview still-extractor rasterizes it the same way before ffmpeg sees
313
+ it — ffmpeg itself has no svg decoder, so never hand an svg path to it directly.
314
+ A still stand-in in a video slot keeps a no-footage render a poster (PNG);
315
+ motion arrives with the real clip.
316
+
317
+ Make marks an EXISTING slot with no path (a starter showing) with a persistent
318
+ dashed outline and an "Add …" glyph. An add handle (`teasers[len]`, an unborn
319
+ row) is not a slot and stays quiet — otherwise every empty date cell would light.
320
+
321
+ ## What Make does with them (the host side, for orientation)
322
+
323
+ - Badge row on hover: one glyph per kind (T · 123 · swatch · move · picture),
324
+ collapsing to one dot on a tile under ~60 on-screen px. A click on a glyph is
325
+ a double-click WITH that intent.
326
+ - Text / number / colour → the inline form over the rect (Enter / blur commit,
327
+ Esc cancels, an unchanged draft writes nothing). Rect → the regions draw
328
+ session seeded from the painted cell. Media → the chip; a drop needs no click.
329
+ - Every write goes through the ONE prop writer, so it survives every other
330
+ edit and re-renders the template; the panel field updates because it IS the
331
+ prop.
332
+ - The double-clicked tile is often chrome stacked over the bound rect (gridlines,
333
+ glass) — Make looks THROUGH the stack under the pointer for the topmost bound
334
+ tile, for clicks and drops alike.
335
+
336
+ First implementers: `@m0saic-dev/creator/drop-calendar/v1` (every kind, the
337
+ companion, the facecam slot); `@m0saic/alpine/*` (text / number / colour);
338
+ `@m0saic-dev/music/lyric-video/v1` (ranges).
@@ -0,0 +1,236 @@
1
+ # Template status flags: `internal` / `primitive` / `deprecated`
2
+
3
+ Lookup reference for the three orthogonal discovery/visibility flags on
4
+ `MosaicTemplate`. A template may carry any combination; conflating them is the
5
+ standard mistake. Declared in https://github.com/m0saic-project/m0saic-packages/blob/main/packages/types/src/template/template.ts —
6
+ `internal?`, `primitive?`, `deprecated?` (a `MosaicTemplateDeprecation`), each
7
+ with its truth table in the JSDoc (re-verify:
8
+ `grep -n "internal?: boolean\|primitive?: boolean\|deprecated?: MosaicTemplateDeprecation" https://github.com/m0saic-project/m0saic-packages/blob/main/packages/types/src/template/template.ts).
9
+
10
+ **All three are CATALOG fields (0.3.1).** They describe a template without
11
+ changing what it renders, so they are declared in the template's catalog
12
+ sidecar — `<name>.catalog.json` beside its module — never in its code
13
+ (`catalogSidecar`, throw). The registry applies the sidecar catalog-first when
14
+ the template is defined, so hosts read `template.internal` / `.primitive` /
15
+ `.deprecated` exactly as before. Templates shipped before 0.3.1 keep the values
16
+ in their (frozen) code; a sidecar entry overrides them. The same goes for
17
+ `label`, `description`, `tags`, `platforms`, `channels` and `preview`.
18
+
19
+ | Flag | Question | Renderable alone? | Default UI |
20
+ |---|---|---|---|
21
+ | `primitive?: boolean` | "Is this a base others build on?" | **Yes** | Shown + **PRIMITIVE** badge (teal) |
22
+ | `internal?: boolean` | "Is this intended as a **top-level selection**?" (No → hidden) | **Sometimes** — the flag doesn't decide this | **Hidden**; **INTERNAL** badge (violet) when revealed |
23
+ | `deprecated?: MosaicTemplateDeprecation` (`{reason?, replacement?, since?}`) | "Is this still the recommended pick?" | Yes | **Hidden**; **DEPRECATED** tag + "use X instead" |
24
+
25
+ **`primitive` is informational — it does NOT hide anything.** `internal` and
26
+ `deprecated` are the visibility gates.
27
+
28
+ ## Why `primitive` ≠ `internal`
29
+
30
+ - **`primitive` is a quality** — foundational, widely reused. `charts/donut`,
31
+ `charts/stat-card`, `charts/bar-graph`, `primitives/grid` are primitives *and*
32
+ perfectly good standalone picks.
33
+ - **`internal` is an INTENT/visibility flag, not a capability statement** — it says
34
+ "not intended as a top-level pick": excluded from public listings and template
35
+ pickers by default, but fully available to `renderNestedTemplate()`
36
+ (`internal` JSDoc, `template.ts:1375-1384`). Whether it can render standalone
37
+ varies by template and is NOT what the flag encodes: the ffmpeg-pulse piece
38
+ templates are internal yet render fine on their own; `bar-graph`'s `bar-cell`
39
+ leaf is internal AND genuinely expects parent-injected context. Don't infer
40
+ either way from the flag — read the template.
41
+ - **Both** is a real combination: a shared building block many templates reuse
42
+ that still isn't meant to be picked top-level.
43
+
44
+ (The `primitive` flag's orthogonality table in `template.ts` used to gloss
45
+ `internal` as "can this render on its own?", contradicting the `internal` JSDoc —
46
+ fixed in-code 2026-07-27; both JSDoc now carry the top-level-selection semantics.)
47
+
48
+ ## Where the flags surface
49
+
50
+ - **Electron `templates:get`** (the Mosaic Desktop / Web app source (not published)) serializes all
51
+ three into the meta the desktop Templates page consumes.
52
+ - **Desktop Templates page** — PRIMITIVE / INTERNAL badges with explanatory tooltips.
53
+ - **CLI `list-templates` / `browse-templates`** — annotates
54
+ `(primitive) (internal) (deprecated)` in that **fixed order**
55
+ (the CLI source (not published)-33`).
56
+
57
+ ## Curation is a content decision
58
+
59
+ `primitive` started as 4 seeded entries and is on **29 templates** as of 2026-07-27
60
+ (re-verify: `grep -rn "primitive: true" https://github.com/m0saic-project/m0saic-packages/blob/main/packages/templates/src/m0saic | wc -l`).
61
+ There is no rule that derives it — like `deprecated`, someone decides. If you're
62
+ adding a template that other templates will compose, set it (in its catalog
63
+ sidecar); don't wait for a sweep.
64
+
65
+ ⚠️ **Deprecating obligates a pruning pass** — declare `deprecated` in the old
66
+ version's catalog sidecar (never its frozen code; a shipped deprecation is
67
+ permanent), then repoint the curated registry,
68
+ and remove the id from the CLI E2E cases and the e2e variant list. The full ordered
69
+ procedure is in the maintainers' agent contract §10 (not published) "Deprecating a template"; a deprecated id must
70
+ never linger in those lists. The default e2e sweep auto-excludes deprecated
71
+ templates; opt back in with `M0SAIC_INCLUDE_DEPRECATED=1`.
72
+
73
+ ### Deprecation also silences convention advice (2026-09-09)
74
+
75
+ `M0SAIC_INCLUDE_DEPRECATED=1` governs more than the e2e render sweep: it also
76
+ controls **convention findings** in https://github.com/m0saic-project/m0saic-packages/blob/main/packages/templates/tools/check-registry.mjs,
77
+ which gates `npm run build`.
78
+
79
+ By default a deprecated template's definition-time and render-time convention
80
+ warnings/errors are **suppressed** — the template is defunct, so the advice will
81
+ never be acted on and repeating it forever is noise. This makes the intended
82
+ workflow self-cleaning: for a shipped template that violates a convention, **bump
83
+ to vN+1 and deprecate the old one — the old one goes quiet automatically.** There
84
+ is no exclusion list to maintain.
85
+
86
+ The `outputFormat` convention (2026-09-13: a public template declares
87
+ `outputHints.format`; record posture) treats both visibility gates as
88
+ exemptions at the audit itself, not only through this suppression: an
89
+ `internal` building block renders only nested and a `deprecated` template is
90
+ frozen history, so neither owes a deliverable declaration
91
+ (`auditOutputFormat`, https://github.com/m0saic-project/m0saic-packages/blob/main/packages/template-utils/src/template/auditSchemaConventions.ts).
92
+
93
+ The suppressed count is always printed, never silent:
94
+
95
+ ```
96
+ [check-registry] ℹ 14 finding(s) on 22 deprecated template(s) suppressed
97
+ (advice only — freeze + fingerprints still apply).
98
+ M0SAIC_INCLUDE_DEPRECATED=1 to see them.
99
+ ```
100
+
101
+ ⭐ **Suppression is ADVICE ONLY.** The freeze gate (`frozen.manifest.json`, every
102
+ template shipped in 0.1.0) and the layout fingerprints still cover deprecated
103
+ templates in full, byte for byte, in both modes. Deprecation means "stop
104
+ suggesting improvements" — it NEVER grants permission to change a shipped
105
+ template. See the maintainers' agent contract §10 (not published).
106
+
107
+ Measured when introduced (103 registered templates, 22 deprecated): render-time
108
+ warnings 21 → 12, definition-time warning knobs 208 → 165 across 30 → 25
109
+ templates; freeze (327 files) and fingerprints (89) identical either way.
110
+
111
+ ## The repo's front door — `repo.helloWorld` (2026-09-14)
112
+
113
+ Not a template flag but a **repo-descriptor** field beside them
114
+ (`MosaicTemplateRepoDescriptor.helloWorld?: TemplateId`,
115
+ https://github.com/m0saic-project/m0saic-packages/blob/main/packages/types/src/template-repo/template-repo.ts): the id of the template a
116
+ newcomer renders first. Core names `@m0saic/hello-world/v1`; each starter names
117
+ its own `…/basics/hello-world/v1`. It is deliberately NOT a `TemplateRole` — roles
118
+ say what a template *produces*; this says which one is the door.
119
+
120
+ Readers: the CLI alias (`m0saic hello-world --template-repo <path>`), the manifest
121
+ (the descriptor is embedded, so `template-manifest.json` carries it zero-exec; the
122
+ platform loader passes it through), and — follow-up — Make's "Start here" chip.
123
+ The gate's `repoFrontDoor` convention (record posture) warns when the field is
124
+ unset or names an id the repo does not register; `internal` / `deprecated`
125
+ templates are fine to name, just unusual. Authoring recipe: `standalone-pack-authoring.md`
126
+ §1.
127
+
128
+ ## A canvas that is a knob — `resolveOutputHints` (2026-09-15)
129
+
130
+ Also not a flag but a **template field** beside `outputHints`:
131
+ `resolveOutputHints?: (props) => Partial<MosaicTemplateOutputHints>`. The
132
+ static `outputHints` stay what the manifest and the cards show; the resolver
133
+ is what a host asks with the current props before rendering, through the one
134
+ helper `resolveTemplateOutputHints(tmpl, props)` (`@m0saic/template-utils`).
135
+ Readers: CLI `make` (+ wireframe / tutorial paths), Electron preview / cover /
136
+ render-to-file + the `templates:resolveOutputHints` IPC, the web design
137
+ preview, and Make's Device anchor (re-resolved on every prop change). The
138
+ seam's `outputHintsResolve` convention (THROW posture) checks it returns an
139
+ object, is deterministic at `defaultProps`, and agrees with the static hints
140
+ for every field it returns there. Precedence: [`../output-resolution-tree.md`](../output-resolution-tree.md) §size.
141
+
142
+ **Image or video is part of the same answer (2026-09-26).** `format` rides the same
143
+ resolver. A template whose kind depends on its inputs — a `media` prop that accepts
144
+ both kinds — declares it per props, `resolveOutputHints: (props) => ({ format: … })`,
145
+ and its static `outputHints.format` is that function's value at `defaultProps`, which
146
+ for an EMPTY media slot is **image** even when the template is "for" video (the
147
+ `outputHintsResolve` throw holds the two equal). The resolver is pure and prop-only:
148
+ it runs before any probe, so a path's extension is the evidence available. Exactly
149
+ three answers exist — the static hint at defaults, the resolver at the current
150
+ props, and the user's override — and nothing else authors one. Every host asks the
151
+ author through `resolveTemplateOutputHints(tmpl, props).format` and passes it into
152
+ `createDesignContext` / `createEngineContext` as `format`, so the `format` the
153
+ `defineMosaicTemplate` wrapper stamps on the rendered document is a RECORD of the
154
+ decision, never a source of it. `inferOutputKind` (`@m0saic/platform`) is a last
155
+ resort for a bare `.mosaic` opened cold with no template behind it, and since
156
+ 2026-09-26 it counts a `type: "media"` video source as motion (`videoMedia`) — the
157
+ omission that made footage-through-a-mask read as a still. The defect class and the
158
+ seven-surface audit that produced this rule:
159
+ (internal design history).
160
+
161
+ ## The lattice declarations — `lattice` (2026-09-16)
162
+
163
+ **Convention `latticeSmooth` (throw):** every split count above 12 in the rendered
164
+ layout — root document and every nested child, walked as documents (a nested doc
165
+ whose flatten fails below its floor is still measured) — is 5-smooth, `N = 2ᵃ·3ᵇ·5ᶜ`.
166
+ Counts ≤ 12 with a rough factor (7 weekdays, 11 tiles) pass as small-basis ratio
167
+ fill. Measured at the hinted canvas and, under `--sweep`, on the standard canvases.
168
+ Why: all delivery canvases are 5-smooth with family gcd 120, so 5-smooth counts
169
+ divide them (pixel-exact), any two 5-smooth self-lattices LCM to a small number,
170
+ and a child never inherits a rough cell size (handbook
171
+ [`composition-arithmetic.md`](../../handbook/composition-arithmetic.md) §1–3).
172
+ `m0saic doctor <repo>` reports it for external packs at error severity — the
173
+ publish requirement. Plan + numbers: (internal design history).
174
+
175
+ `MosaicTemplate.lattice?` (https://github.com/m0saic-project/m0saic-packages/blob/main/packages/types/src/template/template.ts, beside
176
+ `primitive?` / `deprecated?`) is the declaration surface — every override is typed,
177
+ reasoned, and printed by the gate:
178
+
179
+ | field | when it is honest | effect |
180
+ |---|---|---|
181
+ | `mode: "bitmap"` | the split counts ARE a raster (QR modules, barcode bars, a logo trace, the community M) — handbook §3c BITMAP, baked once, never live-composed | the template is skipped with a note |
182
+ | `allow: [{ count, reason }]` | a count above 12 that is content cardinality (53 ISO weeks; a 335-tile fixture grid) | the count is accepted; the reason is printed and carried in `--json` |
183
+ | `canvas: "physical"` | the hinted size is a millimetre spec at a dpi (business card 1130×678 = 2·5·113 × 2·3·113) | the rough-axis finding is not raised, counts the canvas hands down are charged to it (not to the construction), and the `--sweep` canvases are skipped — a print size cannot move |
184
+
185
+ Per DOCUMENT, `MosaicEngineMeta.lattice?: { mode: "bitmap" }` (`doc.engine.lattice`)
186
+ marks a raster a parent assembles itself (the business card's QR child, built from
187
+ `qrToRenderable` pieces) — that subtree is skipped, the parent is still measured.
188
+ (The layout-fingerprint sidecar cannot carry this marker: the fleet lock
189
+ https://github.com/m0saic-project/m0saic-packages/blob/main/packages/templates/src/lattice-audit.test.ts re-measures such a template with
190
+ `auditRenderedTemplate` instead of trusting the flattened string.)
191
+
192
+ **Not honest:** a count that came from your arithmetic — independent rounding to a
193
+ cap (121/119/241/245), a pitch matched to a grid that no longer exists (499),
194
+ `round(side/K)` + `ceil` self-frames (99/101), pixel splits inside an already
195
+ quantized cell. The finding names the nearest 5-smooth neighbours and the LCM cost;
196
+ the neighbour almost always looks identical.
197
+
198
+ ## Reading the dictionary from a template — web vs node (2026-09-16)
199
+
200
+ `@m0saic/dictionary` has two entries: node (`index.ts`, reads `.m0`/`.m0c` from
201
+ disk) and browser (`browser.ts`, what the Mosaic Desktop / Web app source (not published) bundles via the package's
202
+ `browser` field). Every jest suite resolves the NODE entry, so a template that reads
203
+ a brand entry synchronously can be green under test and an error mosaic on
204
+ app.m0saic.io — which is exactly what QR Code's centre M, Brand Marks v3 and
205
+ community-m did until 2026-09-16 (the browser entry shipped every brand `m0` as `""`
206
+ and its `getRankSet` threw). Rules that hold on BOTH entries now:
207
+
208
+ - **`entry.m0` is populated for every entry whose canonical m0 is ≤ 40 KB**
209
+ (`INLINE_M0_MAX_CHARS` in https://github.com/m0saic-project/m0saic-packages/blob/main/packages/dictionary/tools/validate.js, which emits a
210
+ committed sibling `m0.json` per entry that `browser.ts` imports). Only
211
+ `brand/m-33_bitmap` (149 KB) ships `m0: ""` on web — a template that can take it
212
+ must guard `if (!entry.m0)` and say "Desktop renders this" (Brand Marks v3 does).
213
+ - **`entry.masks`, `entry.rankSets`, `entry.sourceCount` ride in `metadata.json`** →
214
+ identical on web. `registry.getRankSet(id, name)` works on web (same lookup as
215
+ node). Named mask SETS (`getMaskSet`) are node-only.
216
+ - **The guards, in order of reach:** https://github.com/m0saic-project/m0saic-packages/blob/main/packages/dictionary/src/browser.test.ts
217
+ (browser m0 == node m0 for every inlined entry; only the bitmap lazy) ·
218
+ https://github.com/m0saic-project/m0saic-packages/blob/main/packages/templates/src/webDictionary.test.ts (renders QR Code / Brand Marks /
219
+ community-m against the browser entry via `jest.mock`) · the Playwright web lane
220
+ the Mosaic Desktop / Web app source (not published) (the shipped bundle).
221
+ **Add your template to `webDictionary.test.ts` if it reads a brand entry.**
222
+
223
+ the Mosaic Desktop / Web app source (not published) copies `*.json`, so `m0.json`
224
+ also lands under `public/dictionary/` — harmless (≤ 10 KB each). The light entries in
225
+ `browser.ts` are still hand-written F-form literals; the drift test compares them
226
+ canonically to the generated `m0.json`.
227
+
228
+ ## Known wrinkle
229
+
230
+ The Templates page's **"Show layout primitives" toggle** (renamed from "Show
231
+ primitives" on 2026-09-16 so the label says what it does) gates a *tag-derived*
232
+ Primitives **category** (tags `grid` / `primitive` — in practice the `primitives/`
233
+ family) — separate from the cross-cutting `primitive` **flag**, which only ranks and
234
+ badges. They coexist cleanly, but they are two mechanisms with one word. Driving the
235
+ toggle off the flag would hide most of the alpine/charts shelf by default, so the
236
+ founder kept the category gate and fixed the label instead.