@hraness/slopcamera 3.2.4

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 +21 -0
  2. package/NOTICE.md +60 -0
  3. package/PRIVACY.md +46 -0
  4. package/README.md +311 -0
  5. package/SECURITY.md +21 -0
  6. package/apps/desktop/README.md +966 -0
  7. package/apps/desktop/analysis/protocol.ts +223 -0
  8. package/apps/desktop/application/capability-binding.ts +594 -0
  9. package/apps/desktop/application/context.ts +97 -0
  10. package/apps/desktop/application/creative-iteration.ts +1385 -0
  11. package/apps/desktop/application/default-registry.ts +146 -0
  12. package/apps/desktop/application/directing-contract.ts +218 -0
  13. package/apps/desktop/application/directing-plan.ts +178 -0
  14. package/apps/desktop/application/errors.ts +18 -0
  15. package/apps/desktop/application/gateway-port.ts +867 -0
  16. package/apps/desktop/application/html-overlay-browser-runtime.ts +687 -0
  17. package/apps/desktop/application/html-overlay-integrity.ts +337 -0
  18. package/apps/desktop/application/html-overlay-renderer.ts +45 -0
  19. package/apps/desktop/application/index.ts +13 -0
  20. package/apps/desktop/application/operation-completion-checkpoint.ts +333 -0
  21. package/apps/desktop/application/operation-effects.ts +94 -0
  22. package/apps/desktop/application/operation.ts +224 -0
  23. package/apps/desktop/application/operations/analysis/faces.ts +244 -0
  24. package/apps/desktop/application/operations/analysis/music.ts +248 -0
  25. package/apps/desktop/application/operations/analysis/project-auto-zooms.ts +302 -0
  26. package/apps/desktop/application/operations/analysis/project-inactivity.ts +381 -0
  27. package/apps/desktop/application/operations/analysis/scenes.ts +272 -0
  28. package/apps/desktop/application/operations/analysis/shared.ts +202 -0
  29. package/apps/desktop/application/operations/derive/edit-batch.ts +967 -0
  30. package/apps/desktop/application/operations/derive/follow-faces.ts +454 -0
  31. package/apps/desktop/application/operations/edit/bind-candidate-revision.ts +91 -0
  32. package/apps/desktop/application/operations/edit/create-candidate-revision.ts +377 -0
  33. package/apps/desktop/application/operations/edit/create-revision.ts +255 -0
  34. package/apps/desktop/application/operations/edit/freeze-revision.ts +182 -0
  35. package/apps/desktop/application/operations/gateway/image.ts +41 -0
  36. package/apps/desktop/application/operations/gateway/index.ts +17 -0
  37. package/apps/desktop/application/operations/gateway/shared.ts +122 -0
  38. package/apps/desktop/application/operations/gateway/speech.ts +41 -0
  39. package/apps/desktop/application/operations/gateway/transcription.ts +42 -0
  40. package/apps/desktop/application/operations/gateway/video.ts +41 -0
  41. package/apps/desktop/application/operations/index.ts +31 -0
  42. package/apps/desktop/application/operations/iteration/create-candidate.ts +240 -0
  43. package/apps/desktop/application/operations/iteration/create-matrix.ts +117 -0
  44. package/apps/desktop/application/operations/iteration/select.ts +155 -0
  45. package/apps/desktop/application/operations/iteration/shared.ts +250 -0
  46. package/apps/desktop/application/operations/media/audio-effects.ts +376 -0
  47. package/apps/desktop/application/operations/media/binding.ts +79 -0
  48. package/apps/desktop/application/operations/media/capabilities.ts +112 -0
  49. package/apps/desktop/application/operations/media/color-grade.ts +343 -0
  50. package/apps/desktop/application/operations/media/html-overlay.ts +802 -0
  51. package/apps/desktop/application/operations/media/index.ts +8 -0
  52. package/apps/desktop/application/operations/media/ingest.ts +303 -0
  53. package/apps/desktop/application/operations/media/overlay.ts +729 -0
  54. package/apps/desktop/application/operations/media/shared.ts +845 -0
  55. package/apps/desktop/application/operations/media/transform-platform.ts +120 -0
  56. package/apps/desktop/application/operations/project/commit-edits.ts +2054 -0
  57. package/apps/desktop/application/operations/project/promote-selection.ts +495 -0
  58. package/apps/desktop/application/operations/project/snapshot.ts +95 -0
  59. package/apps/desktop/application/operations/recording/index.ts +5 -0
  60. package/apps/desktop/application/operations/recording/pause.ts +34 -0
  61. package/apps/desktop/application/operations/recording/resume.ts +34 -0
  62. package/apps/desktop/application/operations/recording/shared.ts +199 -0
  63. package/apps/desktop/application/operations/recording/start.ts +169 -0
  64. package/apps/desktop/application/operations/recording/stop.ts +34 -0
  65. package/apps/desktop/application/operations/render/bind-candidate-output.ts +324 -0
  66. package/apps/desktop/application/operations/render/materialize-selection.ts +322 -0
  67. package/apps/desktop/application/operations/render/project-caption-plan.ts +287 -0
  68. package/apps/desktop/application/operations/render/project-plan.ts +939 -0
  69. package/apps/desktop/application/operations/render/project-spatial-receipt.ts +70 -0
  70. package/apps/desktop/application/operations/render/project.ts +1922 -0
  71. package/apps/desktop/application/operations/shared.ts +67 -0
  72. package/apps/desktop/application/operations/slopcamera-portable.ts +1188 -0
  73. package/apps/desktop/application/operations/slopcamera-visuals.ts +784 -0
  74. package/apps/desktop/application/operations/spatial-project.ts +209 -0
  75. package/apps/desktop/application/operations/spatial-render.ts +415 -0
  76. package/apps/desktop/application/operations/spatial-scene.ts +97 -0
  77. package/apps/desktop/application/operations/studio.ts +143 -0
  78. package/apps/desktop/application/output-publication-lease.ts +163 -0
  79. package/apps/desktop/application/project-publication-lease.ts +233 -0
  80. package/apps/desktop/application/project-store.ts +196 -0
  81. package/apps/desktop/application/receipts.ts +533 -0
  82. package/apps/desktop/application/registry.ts +182 -0
  83. package/apps/desktop/application/spatial-assets.ts +675 -0
  84. package/apps/desktop/application/spatial-compositor-cadence.ts +23 -0
  85. package/apps/desktop/application/spatial-project-authority.ts +35 -0
  86. package/apps/desktop/application/spatial-project-lease.ts +126 -0
  87. package/apps/desktop/application/spatial-project-render-preparation.ts +225 -0
  88. package/apps/desktop/application/spatial-project-store.ts +286 -0
  89. package/apps/desktop/application/spatial-render-projection.ts +201 -0
  90. package/apps/desktop/application/spatial-render.ts +677 -0
  91. package/apps/desktop/application/spatial-spz.ts +44 -0
  92. package/apps/desktop/application/spatial-world-import.ts +135 -0
  93. package/apps/desktop/application/spatial-world-provenance.ts +46 -0
  94. package/apps/desktop/application/studio-port.ts +58 -0
  95. package/apps/desktop/application/verified-receipt-reconciliation.ts +1574 -0
  96. package/apps/desktop/capture/protocol.ts +689 -0
  97. package/apps/desktop/cli/analyzer.ts +406 -0
  98. package/apps/desktop/cli/args.ts +3181 -0
  99. package/apps/desktop/cli/asset-ingest.ts +747 -0
  100. package/apps/desktop/cli/atomic-render-effects.ts +210 -0
  101. package/apps/desktop/cli/atomic-render-platform.ts +205 -0
  102. package/apps/desktop/cli/atomic-render.ts +50 -0
  103. package/apps/desktop/cli/audio-analysis.ts +252 -0
  104. package/apps/desktop/cli/bounded-map.ts +39 -0
  105. package/apps/desktop/cli/bundle-service.ts +256 -0
  106. package/apps/desktop/cli/capabilities.ts +212 -0
  107. package/apps/desktop/cli/capture-bundle.ts +1117 -0
  108. package/apps/desktop/cli/command-host-resources.ts +251 -0
  109. package/apps/desktop/cli/commands.ts +6836 -0
  110. package/apps/desktop/cli/directing-blob.ts +291 -0
  111. package/apps/desktop/cli/directing-media.ts +517 -0
  112. package/apps/desktop/cli/directing-quote.ts +89 -0
  113. package/apps/desktop/cli/directing-service.ts +288 -0
  114. package/apps/desktop/cli/emoji-assets.ts +538 -0
  115. package/apps/desktop/cli/errors.ts +29 -0
  116. package/apps/desktop/cli/face-analysis-service.ts +940 -0
  117. package/apps/desktop/cli/filter-script.ts +76 -0
  118. package/apps/desktop/cli/gateway-application-port.ts +1142 -0
  119. package/apps/desktop/cli/gateway-credential.ts +81 -0
  120. package/apps/desktop/cli/gateway-media-artifacts.ts +723 -0
  121. package/apps/desktop/cli/gateway-media-catalog.ts +922 -0
  122. package/apps/desktop/cli/gateway-media-service.ts +3106 -0
  123. package/apps/desktop/cli/gateway-media-signature.ts +122 -0
  124. package/apps/desktop/cli/gateway-provider-options.ts +562 -0
  125. package/apps/desktop/cli/gateway-scene-provider.ts +391 -0
  126. package/apps/desktop/cli/help.ts +501 -0
  127. package/apps/desktop/cli/html-overlay-gpu.ts +97 -0
  128. package/apps/desktop/cli/html-overlay-renderer.ts +2478 -0
  129. package/apps/desktop/cli/io.ts +290 -0
  130. package/apps/desktop/cli/main.ts +123 -0
  131. package/apps/desktop/cli/media-effects-service.ts +885 -0
  132. package/apps/desktop/cli/media-ingest-model.ts +364 -0
  133. package/apps/desktop/cli/media-ingest-platform.ts +227 -0
  134. package/apps/desktop/cli/media-ingest-program.ts +200 -0
  135. package/apps/desktop/cli/media-ingest.ts +30 -0
  136. package/apps/desktop/cli/music-analysis-service.ts +434 -0
  137. package/apps/desktop/cli/mutation-lock.ts +530 -0
  138. package/apps/desktop/cli/native-assets.d.ts +12 -0
  139. package/apps/desktop/cli/native-media-runtime.macos.ts +7 -0
  140. package/apps/desktop/cli/overlay-playback.ts +177 -0
  141. package/apps/desktop/cli/paths.ts +265 -0
  142. package/apps/desktop/cli/portable-surface.ts +242 -0
  143. package/apps/desktop/cli/project-camera-receipt.ts +112 -0
  144. package/apps/desktop/cli/project-face-camera.ts +394 -0
  145. package/apps/desktop/cli/project-inactivity-service.ts +552 -0
  146. package/apps/desktop/cli/project-media-integrity.ts +121 -0
  147. package/apps/desktop/cli/project-renderer.ts +1526 -0
  148. package/apps/desktop/cli/project-service.ts +519 -0
  149. package/apps/desktop/cli/project-state-transaction.ts +520 -0
  150. package/apps/desktop/cli/query-limits.ts +1 -0
  151. package/apps/desktop/cli/recording-controller.ts +1567 -0
  152. package/apps/desktop/cli/recording-daemon.ts +931 -0
  153. package/apps/desktop/cli/recording-ref.ts +56 -0
  154. package/apps/desktop/cli/renamed-environment.ts +6 -0
  155. package/apps/desktop/cli/renderer.ts +1439 -0
  156. package/apps/desktop/cli/run-cli-test-helper.ts +60 -0
  157. package/apps/desktop/cli/scene-analysis-service.ts +879 -0
  158. package/apps/desktop/cli/spatial-compositor-verifier.ts +213 -0
  159. package/apps/desktop/cli/spatial-project-service.ts +90 -0
  160. package/apps/desktop/cli/spatial-scene-service.ts +105 -0
  161. package/apps/desktop/cli/spatial-world-service.ts +39 -0
  162. package/apps/desktop/cli/speech-analysis-service.ts +682 -0
  163. package/apps/desktop/cli/studio-assemble.ts +58 -0
  164. package/apps/desktop/cli/studio-command.ts +60 -0
  165. package/apps/desktop/cli/studio-custody.ts +60 -0
  166. package/apps/desktop/cli/studio-encode.ts +216 -0
  167. package/apps/desktop/cli/studio-exr.ts +52 -0
  168. package/apps/desktop/cli/studio-files.ts +179 -0
  169. package/apps/desktop/cli/studio-output-validation.ts +162 -0
  170. package/apps/desktop/cli/studio-process.ts +145 -0
  171. package/apps/desktop/cli/studio-runtime.ts +77 -0
  172. package/apps/desktop/cli/studio-scaffold.ts +70 -0
  173. package/apps/desktop/cli/studio-service.ts +301 -0
  174. package/apps/desktop/cli/studio-spatial-asset.ts +248 -0
  175. package/apps/desktop/cli/studio-template-names.ts +3 -0
  176. package/apps/desktop/cli/studio-workflow.ts +19 -0
  177. package/apps/desktop/cli/time.ts +25 -0
  178. package/apps/desktop/cli/workflow-code.ts +584 -0
  179. package/apps/desktop/cli/workflow-runs.ts +576 -0
  180. package/apps/desktop/code/advanced.ts +139 -0
  181. package/apps/desktop/code/application-node-planner.ts +1288 -0
  182. package/apps/desktop/code/compiler.ts +329 -0
  183. package/apps/desktop/code/contracts.ts +13 -0
  184. package/apps/desktop/code/creative-recipes.ts +94 -0
  185. package/apps/desktop/code/define-workflow.ts +115 -0
  186. package/apps/desktop/code/editing.ts +284 -0
  187. package/apps/desktop/code/file-candidate-provenance.ts +420 -0
  188. package/apps/desktop/code/file-candidate.ts +69 -0
  189. package/apps/desktop/code/graph-builder.ts +10 -0
  190. package/apps/desktop/code/host-resource-policy.ts +55 -0
  191. package/apps/desktop/code/plan-contracts.ts +159 -0
  192. package/apps/desktop/code/planning.ts +361 -0
  193. package/apps/desktop/code/public.ts +134 -0
  194. package/apps/desktop/code/reference-projection.ts +62 -0
  195. package/apps/desktop/code/run-contracts.ts +466 -0
  196. package/apps/desktop/code/run-store.ts +2478 -0
  197. package/apps/desktop/code/runtime-identity.ts +328 -0
  198. package/apps/desktop/code/scheduler.ts +3067 -0
  199. package/apps/desktop/code/semantic-builder.ts +1436 -0
  200. package/apps/desktop/code/source-bundle.ts +1641 -0
  201. package/apps/desktop/code/source-typecheck.ts +211 -0
  202. package/apps/desktop/code/testing.ts +79 -0
  203. package/apps/desktop/code/worker-client.ts +1675 -0
  204. package/apps/desktop/code/worker-effects.ts +141 -0
  205. package/apps/desktop/code/worker-entry.ts +435 -0
  206. package/apps/desktop/code/worker-lease-guardian.ts +179 -0
  207. package/apps/desktop/code/worker-process-identity.ts +484 -0
  208. package/apps/desktop/code/worker-protocol.ts +211 -0
  209. package/apps/desktop/code/workflow-effects.ts +132 -0
  210. package/apps/desktop/contracts/analysis.ts +949 -0
  211. package/apps/desktop/contracts/edit.ts +576 -0
  212. package/apps/desktop/contracts/index.ts +9 -0
  213. package/apps/desktop/contracts/media-effects.ts +139 -0
  214. package/apps/desktop/contracts/project-render.ts +644 -0
  215. package/apps/desktop/contracts/project.ts +740 -0
  216. package/apps/desktop/contracts/recording.ts +1434 -0
  217. package/apps/desktop/contracts/render.ts +334 -0
  218. package/apps/desktop/contracts/runtime.ts +155 -0
  219. package/apps/desktop/contracts/spatial-compositor.ts +58 -0
  220. package/apps/desktop/contracts/spatial-project.ts +159 -0
  221. package/apps/desktop/contracts/spatial-world.ts +48 -0
  222. package/apps/desktop/contracts/video-effects.ts +120 -0
  223. package/apps/desktop/core/alignment-apply.ts +400 -0
  224. package/apps/desktop/core/audio-alignment.ts +466 -0
  225. package/apps/desktop/core/auto-zoom.ts +209 -0
  226. package/apps/desktop/core/canonical-json.ts +14 -0
  227. package/apps/desktop/core/emoji.ts +210 -0
  228. package/apps/desktop/core/events.ts +409 -0
  229. package/apps/desktop/core/face-framing.ts +533 -0
  230. package/apps/desktop/core/face-tracks.ts +326 -0
  231. package/apps/desktop/core/inactivity.ts +137 -0
  232. package/apps/desktop/core/index.ts +30 -0
  233. package/apps/desktop/core/intervals.ts +123 -0
  234. package/apps/desktop/core/montage.ts +236 -0
  235. package/apps/desktop/core/music-analysis.ts +518 -0
  236. package/apps/desktop/core/plan.ts +244 -0
  237. package/apps/desktop/core/project-auto-zoom.ts +75 -0
  238. package/apps/desktop/core/project-camera-spatial.ts +676 -0
  239. package/apps/desktop/core/project-camera.ts +403 -0
  240. package/apps/desktop/core/project-captions.ts +888 -0
  241. package/apps/desktop/core/project-inactivity.ts +125 -0
  242. package/apps/desktop/core/project-metadata.ts +927 -0
  243. package/apps/desktop/core/project-plan.ts +375 -0
  244. package/apps/desktop/core/project-render-plan.ts +303 -0
  245. package/apps/desktop/core/project-time.ts +469 -0
  246. package/apps/desktop/core/render-plan.ts +442 -0
  247. package/apps/desktop/core/scene-cache.ts +174 -0
  248. package/apps/desktop/core/scene-sampling.ts +448 -0
  249. package/apps/desktop/core/spatial-compositor.ts +22 -0
  250. package/apps/desktop/core/spatial-durability.ts +99 -0
  251. package/apps/desktop/core/spatial-project.ts +169 -0
  252. package/apps/desktop/core/spatial-shot-clock.ts +32 -0
  253. package/apps/desktop/core/speech-analysis.ts +333 -0
  254. package/apps/desktop/core/storage.ts +970 -0
  255. package/apps/desktop/core/time-map.ts +91 -0
  256. package/apps/desktop/core/time.ts +61 -0
  257. package/apps/desktop/core/video-effects.ts +660 -0
  258. package/apps/desktop/dist/cli/NebulaSans-Bold-26se8aek.otf +0 -0
  259. package/apps/desktop/dist/cli/NebulaSans-Bold-bcz7y08t.woff2 +0 -0
  260. package/apps/desktop/dist/cli/NebulaSans-Book-5ax05zvn.woff2 +0 -0
  261. package/apps/desktop/dist/cli/NebulaSans-Book-8cenzchw.otf +0 -0
  262. package/apps/desktop/dist/cli/main.js +5086 -0
  263. package/apps/desktop/html-overlay/catalog.ts +171 -0
  264. package/apps/desktop/html-overlay/contracts.ts +372 -0
  265. package/apps/desktop/html-overlay/execution-profile.ts +77 -0
  266. package/apps/desktop/html-overlay/index.ts +98 -0
  267. package/apps/desktop/html-overlay/libraries.ts +263 -0
  268. package/apps/desktop/html-overlay/random.ts +49 -0
  269. package/apps/desktop/html-overlay/runtime.ts +813 -0
  270. package/apps/desktop/html-overlay/scaffolds.ts +792 -0
  271. package/apps/desktop/html-overlay/spark-runtime.ts +88 -0
  272. package/apps/desktop/html-overlay/spatial-splat-runtime.ts +60 -0
  273. package/apps/desktop/html-overlay/spatial.ts +720 -0
  274. package/apps/desktop/studio/assets/README.md +39 -0
  275. package/apps/desktop/studio/assets/contracts.ts +129 -0
  276. package/apps/desktop/studio/assets/network.ts +64 -0
  277. package/apps/desktop/studio/assets/poly-haven.ts +148 -0
  278. package/apps/desktop/studio/drivers/blender_driver.py +466 -0
  279. package/apps/desktop/studio/drivers/cadquery_driver.py +176 -0
  280. package/apps/desktop/studio/education/driver.py +308 -0
  281. package/apps/desktop/studio/education/lesson.py +180 -0
  282. package/apps/desktop/studio/education/toolkit.py +249 -0
  283. package/apps/desktop/workflows/chaptered-demo.ts +48 -0
  284. package/apps/desktop/workflows/creative-iteration.ts +107 -0
  285. package/apps/desktop/workflows/creative-selection.ts +92 -0
  286. package/apps/desktop/workflows/fragments.ts +486 -0
  287. package/apps/desktop/workflows/index.ts +93 -0
  288. package/apps/desktop/workflows/polished-screen-demo.ts +128 -0
  289. package/apps/desktop/workflows/scene-direction.ts +12 -0
  290. package/apps/desktop/workflows/social-variants.ts +75 -0
  291. package/apps/desktop/workflows/talking-head-cleanup.ts +53 -0
  292. package/dist/NebulaSans-Bold-26se8aek.otf +0 -0
  293. package/dist/NebulaSans-Bold-bcz7y08t.woff2 +0 -0
  294. package/dist/NebulaSans-Book-5ax05zvn.woff2 +0 -0
  295. package/dist/NebulaSans-Book-8cenzchw.otf +0 -0
  296. package/dist/cli.js +479 -0
  297. package/dist/code/advanced.js +270 -0
  298. package/dist/code/index.js +2240 -0
  299. package/dist/generate.js +24 -0
  300. package/dist/host-resources.js +26 -0
  301. package/dist/index-42zsesc1.js +2257 -0
  302. package/dist/index-7308egqr.js +48 -0
  303. package/dist/index-8txs6fkn.js +712 -0
  304. package/dist/index-mcy8z0br.js +192 -0
  305. package/dist/index-p63wavx0.js +2029 -0
  306. package/dist/index-r7gdhmsp.js +424 -0
  307. package/dist/index-sh6xbav6.js +1219 -0
  308. package/dist/index-z1w83f81.js +4 -0
  309. package/dist/index-z7239b4h.js +2363 -0
  310. package/dist/index-zfeg9hmw.js +2013 -0
  311. package/dist/index.js +241 -0
  312. package/dist/operations.js +29 -0
  313. package/dist/skill-install-dh0ntqba.js +10 -0
  314. package/dist/vectorize/worker.js +142 -0
  315. package/dist/workflow.js +16 -0
  316. package/docs/README.md +38 -0
  317. package/docs/studio.md +215 -0
  318. package/examples/studio/blender/README.md +26 -0
  319. package/examples/studio/blender/character.py +152 -0
  320. package/examples/studio/blender/cloth.py +101 -0
  321. package/examples/studio/blender/color_chart.py +29 -0
  322. package/examples/studio/blender/fluid.py +78 -0
  323. package/examples/studio/blender/import_model.py +74 -0
  324. package/examples/studio/blender/product.py +53 -0
  325. package/examples/studio/blender/shaded_street.py +481 -0
  326. package/examples/studio/blender/studio_scene.py +147 -0
  327. package/examples/studio/cadquery/README.md +13 -0
  328. package/examples/studio/cadquery/bracket.py +36 -0
  329. package/examples/studio/cadquery/import_step.py +12 -0
  330. package/examples/studio/education/README.md +46 -0
  331. package/examples/studio/education/lesson.json +99 -0
  332. package/examples/studio/education/scene.py +7 -0
  333. package/examples/studio/hybrid-scene.ts +96 -0
  334. package/examples/studio/native-workflow.ts +14 -0
  335. package/examples/studio/vgpu/README.md +64 -0
  336. package/examples/studio/vgpu/heat-field.mjs +290 -0
  337. package/package.json +304 -0
  338. package/packages/scene/src/index.ts +1 -0
  339. package/packages/scene/src/scene.ts +215 -0
  340. package/schema/diagram.schema.json +334 -0
  341. package/skills/slopcamera/SKILL.md +45 -0
  342. package/skills/slopcamera/agents/openai.yaml +4 -0
  343. package/skills/slopcamera/references/customization.md +77 -0
  344. package/skills/slopcamera/references/diagrams.md +153 -0
  345. package/skills/slopcamera/references/directed-scenes.md +42 -0
  346. package/skills/slopcamera/references/directing-video.md +18 -0
  347. package/skills/slopcamera/references/educational-video.md +26 -0
  348. package/skills/slopcamera/references/gateway-media.md +134 -0
  349. package/skills/slopcamera/references/install.md +62 -0
  350. package/skills/slopcamera/references/native-studio.md +31 -0
  351. package/skills/slopcamera/references/reference-led-3d.md +140 -0
  352. package/skills/slopcamera/references/rubber-stamp-examples/poster-example-1.jpg +0 -0
  353. package/skills/slopcamera/references/rubber-stamp-examples/poster-example-2.jpg +0 -0
  354. package/skills/slopcamera/references/rubber-stamp-examples/stamp-style-1.png +0 -0
  355. package/skills/slopcamera/references/rubber-stamp-examples/stamp-style-2.png +0 -0
  356. package/skills/slopcamera/references/rubber-stamp-field-notes.md +144 -0
  357. package/skills/slopcamera/references/vectorization.md +29 -0
  358. package/skills/slopcamera/references/video-projects.md +187 -0
  359. package/skills/slopcamera/references/visual-communication.md +120 -0
  360. package/skills/slopcamera/references/workflows-sdk.md +45 -0
  361. package/skills/slopcamera/scripts/compose-rubber-stamp-field-note.ts +229 -0
  362. package/src/artifacts.ts +106 -0
  363. package/src/assets/fonts/nebula-sans/LICENSE.txt +96 -0
  364. package/src/assets/fonts/nebula-sans/NebulaSans-Bold.otf +0 -0
  365. package/src/assets/fonts/nebula-sans/NebulaSans-Bold.woff2 +0 -0
  366. package/src/assets/fonts/nebula-sans/NebulaSans-Book.otf +0 -0
  367. package/src/assets/fonts/nebula-sans/NebulaSans-Book.woff2 +0 -0
  368. package/src/assets/fonts/nebula-sans/PROVENANCE.md +21 -0
  369. package/src/assets.d.ts +9 -0
  370. package/src/cli.ts +601 -0
  371. package/src/cloud-errors.ts +26 -0
  372. package/src/code/advanced.ts +10 -0
  373. package/src/code/boundary.ts +21 -0
  374. package/src/code/canonical-json.ts +129 -0
  375. package/src/code/compiler.ts +953 -0
  376. package/src/code/contracts.ts +576 -0
  377. package/src/code/define-workflow.ts +334 -0
  378. package/src/code/errors.ts +41 -0
  379. package/src/code/graph-builder.ts +945 -0
  380. package/src/code/index.ts +121 -0
  381. package/src/code/json-snapshot.ts +899 -0
  382. package/src/code/json-utf8.ts +88 -0
  383. package/src/code/portable-builder.ts +136 -0
  384. package/src/code/projection.ts +378 -0
  385. package/src/code/public-operations.ts +450 -0
  386. package/src/code/runtime.ts +580 -0
  387. package/src/code/sha256.ts +36 -0
  388. package/src/config.ts +180 -0
  389. package/src/desktop.ts +253 -0
  390. package/src/fs.ts +10 -0
  391. package/src/generate.ts +704 -0
  392. package/src/host-resource-posix.ts +161 -0
  393. package/src/host-resources.ts +1749 -0
  394. package/src/icons.ts +58 -0
  395. package/src/index.ts +200 -0
  396. package/src/label-layout.ts +130 -0
  397. package/src/layout.ts +413 -0
  398. package/src/lint.ts +127 -0
  399. package/src/mcp/boundary.ts +354 -0
  400. package/src/mcp/index.ts +33 -0
  401. package/src/mcp/server.ts +272 -0
  402. package/src/mcp/tools.ts +963 -0
  403. package/src/mcp/types.ts +71 -0
  404. package/src/operations.ts +775 -0
  405. package/src/parse.ts +861 -0
  406. package/src/process-environment.ts +17 -0
  407. package/src/render.ts +437 -0
  408. package/src/skill-install.ts +49 -0
  409. package/src/spatial-scene/authoring.ts +27 -0
  410. package/src/spatial-scene/camera-track.ts +83 -0
  411. package/src/spatial-scene/contracts.ts +277 -0
  412. package/src/spatial-scene/evaluate.ts +145 -0
  413. package/src/spatial-scene/gltf.ts +558 -0
  414. package/src/spatial-scene/identity.ts +260 -0
  415. package/src/spatial-scene/index.ts +10 -0
  416. package/src/spatial-scene/inspect.ts +68 -0
  417. package/src/spatial-scene/math.ts +280 -0
  418. package/src/spatial-scene/patch.ts +131 -0
  419. package/src/spatial-scene/time.ts +53 -0
  420. package/src/studio/contracts.ts +129 -0
  421. package/src/studio/index.ts +2 -0
  422. package/src/studio/plan.ts +156 -0
  423. package/src/studio/shared.ts +41 -0
  424. package/src/theme.ts +68 -0
  425. package/src/tldr.ts +474 -0
  426. package/src/types.ts +212 -0
  427. package/src/vectorize/archive.ts +101 -0
  428. package/src/vectorize/command.ts +797 -0
  429. package/src/vectorize/index.ts +27 -0
  430. package/src/vectorize/limits.ts +61 -0
  431. package/src/vectorize/metrics.ts +351 -0
  432. package/src/vectorize/pixels.ts +323 -0
  433. package/src/vectorize/supervisor.ts +339 -0
  434. package/src/vectorize/svg.ts +188 -0
  435. package/src/vectorize/tool.ts +542 -0
  436. package/src/vectorize/types.ts +113 -0
  437. package/src/vectorize/vectorize.ts +634 -0
  438. package/src/vectorize/worker-protocol.ts +40 -0
  439. package/src/vectorize/worker.ts +209 -0
  440. package/src/version.ts +1 -0
  441. package/src/workflow.ts +409 -0
@@ -0,0 +1,1288 @@
1
+ import { Effect } from "effect";
2
+ import { bindStudioRunInput } from "../application/operations/studio";
3
+ import { bindSpatialRenderInput } from "../application/operations/spatial-render";
4
+
5
+ import type { ApplicationContext } from "../application/context";
6
+ import { SpatialProjectBasisSchema } from "../contracts/spatial-project";
7
+ import { SpatialProjectSnapshotOutputSchema, SpatialProjectMutationOutputSchema } from "../application/operations/spatial-project";
8
+ import { bindProjectRenderInputV4, reconcileProjectRenderV4, reconcileProjectRenderEffect, type ProjectRenderReconciliation } from "../application/operations/render/project";
9
+ import {
10
+ bindSlopcameraPortableOperationInputV2,
11
+ slopcameraPortableOutputPublicationParent,
12
+ } from "../application/operations/slopcamera-portable";
13
+ import {
14
+ CreativeBaseV1Schema,
15
+ createCreativeBaseV1,
16
+ } from "../application/creative-iteration";
17
+ import { ApplicationError } from "../application/errors";
18
+ import {
19
+ gatewayPortRequestForOperation,
20
+ gatewayRequestId,
21
+ prepareGatewayOperation,
22
+ reconcileGatewayOperation,
23
+ } from "../application/gateway-port";
24
+ import type { OperationKind } from "../application/operation";
25
+ import {
26
+ openLeasedProjectSnapshot,
27
+ withProjectPublicationLease,
28
+ } from "../application/project-publication-lease";
29
+ import {
30
+ openProjectSnapshot,
31
+ projectGenerationSha256FromHashes,
32
+ projectMatchesEditBasis,
33
+ ProjectEditBasisSchema,
34
+ } from "../application/project-store";
35
+ import {
36
+ bindMediaOperationInput,
37
+ bindSlopcameraVisualOperationInput,
38
+ bindAnalysisCapabilityInput,
39
+ bindCreateCreativeCandidateInput,
40
+ bindCreateCandidateRevisionInput,
41
+ bindMaterializeVariantSelectionInput,
42
+ bindPromoteVariantSelectionInput,
43
+ bindCandidateRenderOutputInput,
44
+ bindProjectCommitEditsInputV2FromSnapshot,
45
+ bindProjectCommitEditsInputV3FromSnapshot,
46
+ bindProjectAutoZoomInput,
47
+ bindProjectInactivityInput,
48
+ bindProjectRenderPlanInput,
49
+ bindProjectRenderPlanInputV2,
50
+ bindProjectRenderInput,
51
+ bindProjectRenderInputV2,
52
+ bindProjectRenderInputV3,
53
+ bindProjectRenderToolchain,
54
+ parseRecordingOperationOutput,
55
+ projectEditCommitReceipt,
56
+ projectEditTransactionId,
57
+ reconcileVariantSelectionPromotion,
58
+ reconcileProjectRender,
59
+ summarizeGatewayOperation,
60
+ CommitProjectEditsInputSchema,
61
+ CommitProjectEditsInputV2Schema,
62
+ CommitProjectEditsInputV3Schema,
63
+ ProjectSnapshotOutputSchema,
64
+ type CommitProjectEditsInput,
65
+ type CommitProjectEditsInputV2,
66
+ type CommitProjectEditsInputV3,
67
+ } from "../application/operations";
68
+ import { ProjectEditCommitReceiptSchema } from "../application/receipts";
69
+ import {
70
+ LOCAL_VERIFIED_RECEIPT_OPERATION_KINDS,
71
+ hasLocalVerifiedReceiptReconciler,
72
+ reconcileLocalVerifiedReceiptOperation,
73
+ } from "../application/verified-receipt-reconciliation";
74
+ import {
75
+ PROJECT_STATE_TRANSACTION_PATH,
76
+ ProjectStateTransactionV1Schema,
77
+ } from "../cli/project-state-transaction";
78
+ import { resolveProjectDirectory } from "../cli/project-service";
79
+ import {
80
+ canonicalJson,
81
+ canonicalJsonFingerprint,
82
+ canonicalJsonSha256,
83
+ sha256Hex,
84
+ } from "../core/canonical-json";
85
+ import {
86
+ createNodeBundleFileSystem,
87
+ loadProjectEditPlan,
88
+ loadVideoProject,
89
+ } from "../core/storage";
90
+ import {
91
+ JsonValueSchema,
92
+ isOperationGraphNode,
93
+ type JsonValue,
94
+ } from "./contracts";
95
+ import { assertOperationFileProvenance } from "./file-candidate-provenance";
96
+ import type {
97
+ NodeExecutionBinding,
98
+ NodeExecutionPlanningRequest,
99
+ NodePreparationBinding,
100
+ NodePreparationRequest,
101
+ NodeReconciliation,
102
+ NodeReconciliationRequest,
103
+ SchedulerNodePlanner,
104
+ } from "./scheduler";
105
+ import { workflowBoundary, workflowValidation, type WorkflowFailure } from "./workflow-effects";
106
+
107
+ const ANALYSIS_ID_DOMAIN = "studio.workflow.analysis-id/v1";
108
+ type RecordingOperationKind = Extract<OperationKind, `recording.${string}`>;
109
+ type GatewayOperationKind = Extract<OperationKind, `gateway.${string}`>;
110
+
111
+ export const APPLICATION_VERIFIED_RECEIPT_RECONCILER_KINDS =
112
+ Object.freeze([
113
+ ...LOCAL_VERIFIED_RECEIPT_OPERATION_KINDS,
114
+ "render.project",
115
+ ] as const satisfies readonly OperationKind[]);
116
+
117
+ function isRecordingOperationKind(
118
+ kind: OperationKind,
119
+ ): kind is RecordingOperationKind {
120
+ return kind === "recording.start"
121
+ || kind === "recording.pause"
122
+ || kind === "recording.resume"
123
+ || kind === "recording.stop";
124
+ }
125
+
126
+ function isGatewayOperationKind(
127
+ kind: OperationKind,
128
+ ): kind is GatewayOperationKind {
129
+ return kind === "gateway.image"
130
+ || kind === "gateway.video"
131
+ || kind === "gateway.speech"
132
+ || kind === "gateway.transcription";
133
+ }
134
+
135
+ function jsonObject(value: JsonValue): value is Readonly<Record<string, JsonValue>> {
136
+ return typeof value === "object"
137
+ && value !== null
138
+ && !Array.isArray(value);
139
+ }
140
+
141
+ function projectReference(input: JsonValue): string | undefined {
142
+ if (!jsonObject(input)) return undefined;
143
+ const project = input.project;
144
+ if (typeof project === "string") return project;
145
+ for (const key of ["revision", "plan"] as const) {
146
+ const nested = input[key];
147
+ if (nested !== undefined && jsonObject(nested)) {
148
+ const projectId = nested.projectId;
149
+ if (typeof projectId === "string") return projectId;
150
+ }
151
+ }
152
+ return undefined;
153
+ }
154
+
155
+ function deterministicAnalysisInput(
156
+ request: NodeExecutionPlanningRequest,
157
+ ): JsonValue {
158
+ const input = request.resolvedInput;
159
+ if (
160
+ (
161
+ request.operation.kind !== "analysis.faces"
162
+ && request.operation.kind !== "analysis.music"
163
+ && request.operation.kind !== "analysis.project-inactivity"
164
+ )
165
+ || !jsonObject(input)
166
+ || input.analysisId !== undefined
167
+ ) {
168
+ return input;
169
+ }
170
+ const suffix = sha256Hex(
171
+ `${ANALYSIS_ID_DOMAIN}\0${canonicalJson({
172
+ graphPlanSha256: request.graphPlan.graphPlanSha256,
173
+ nodeKey: request.node.key,
174
+ runId: request.runId,
175
+ })}`,
176
+ ).slice(0, 32);
177
+ return JsonValueSchema.parse({
178
+ ...input,
179
+ analysisId: `analysis_${suffix}`,
180
+ });
181
+ }
182
+
183
+ async function exactOperationInput(
184
+ application: ApplicationContext,
185
+ request: NodeExecutionPlanningRequest,
186
+ ): Promise<JsonValue> {
187
+ let deterministic = deterministicAnalysisInput(request);
188
+ if (request.operation.kind === "slopcamera.studio.run") return JsonValueSchema.parse(await bindStudioRunInput(request.application ?? application, deterministic, request.abortSignal, request.beforePublication));
189
+ if (request.operation.kind === "scene.render") return JsonValueSchema.parse(await bindSpatialRenderInput(application, deterministic));
190
+ if (request.operation.kind === "spatial.project.snapshot") {
191
+ const project = projectReference(deterministic);
192
+ const subject = request.graphPlan.staticBindings.initialSubjects.find(item => item.kind === "spatial-project" && item.id === project);
193
+ if (subject?.kind !== "spatial-project" || !jsonObject(deterministic)) throw new ApplicationError("authorization-required", "Spatial snapshots require an exact project binding established during planning.");
194
+ const exact = JsonValueSchema.parse({ ...deterministic, expected: deterministic.expected ?? subject.basis });
195
+ assertSpatialProjectBasisProvenance(request, exact);
196
+ return exact;
197
+ }
198
+ if (request.operation.kind === "edit.create-candidate-revision") {
199
+ return JsonValueSchema.parse(
200
+ await bindCreateCandidateRevisionInput(application, deterministic),
201
+ );
202
+ }
203
+ if (request.operation.kind === "iteration.create-candidate") {
204
+ return JsonValueSchema.parse(
205
+ await bindCreateCreativeCandidateInput(application, deterministic),
206
+ );
207
+ }
208
+ if (request.operation.kind === "project.promote-selection") {
209
+ return JsonValueSchema.parse(
210
+ bindPromoteVariantSelectionInput(application, deterministic),
211
+ );
212
+ }
213
+ if (request.operation.kind === "render.materialize-selection") {
214
+ return JsonValueSchema.parse(
215
+ bindMaterializeVariantSelectionInput(application, deterministic),
216
+ );
217
+ }
218
+ if (request.operation.kind === "render.bind-candidate-output") {
219
+ return JsonValueSchema.parse(bindCandidateRenderOutputInput(
220
+ deterministic,
221
+ await bindProjectRenderToolchain(application),
222
+ ));
223
+ }
224
+ if (
225
+ request.operation.kind === "project.commit-edits"
226
+ && request.operation.version === 2
227
+ ) {
228
+ const parsed = CommitProjectEditsInputV2Schema.parse(deterministic);
229
+ const snapshot = await openLeasedProjectSnapshot(
230
+ application,
231
+ parsed.project,
232
+ );
233
+ deterministic = JsonValueSchema.parse(
234
+ await bindProjectCommitEditsInputV2FromSnapshot(
235
+ application,
236
+ parsed,
237
+ snapshot,
238
+ ),
239
+ );
240
+ }
241
+ if (
242
+ request.operation.kind === "project.commit-edits"
243
+ && request.operation.version === 3
244
+ ) {
245
+ const parsed = CommitProjectEditsInputV3Schema.parse(deterministic);
246
+ const snapshot = await openLeasedProjectSnapshot(
247
+ application,
248
+ parsed.project,
249
+ );
250
+ deterministic = JsonValueSchema.parse(
251
+ await bindProjectCommitEditsInputV3FromSnapshot(
252
+ application,
253
+ parsed,
254
+ snapshot,
255
+ ),
256
+ );
257
+ }
258
+ if (
259
+ request.operation.kind === "project.commit-edits"
260
+ && jsonObject(deterministic)
261
+ && deterministic.updatedAt === undefined
262
+ ) {
263
+ deterministic = JsonValueSchema.parse({
264
+ ...deterministic,
265
+ updatedAt: application.clock.now().toISOString(),
266
+ });
267
+ }
268
+ if (request.operation.kind === "analysis.project-auto-zooms") {
269
+ return JsonValueSchema.parse(
270
+ await bindProjectAutoZoomInput(application, deterministic),
271
+ );
272
+ }
273
+ if (
274
+ request.operation.kind === "analysis.faces"
275
+ || request.operation.kind === "analysis.music"
276
+ || request.operation.kind === "analysis.scenes"
277
+ ) {
278
+ return JsonValueSchema.parse(
279
+ await bindAnalysisCapabilityInput(
280
+ application,
281
+ request.operation.kind,
282
+ deterministic,
283
+ ),
284
+ );
285
+ }
286
+ if (request.operation.kind === "analysis.project-inactivity") {
287
+ return JsonValueSchema.parse(
288
+ await bindProjectInactivityInput(application, deterministic),
289
+ );
290
+ }
291
+ if (request.operation.kind === "render.project-plan") {
292
+ if (request.operation.version === 1) {
293
+ return JsonValueSchema.parse(
294
+ await bindProjectRenderPlanInput(application, deterministic),
295
+ );
296
+ }
297
+ if (request.operation.version === 2) {
298
+ return JsonValueSchema.parse(
299
+ await bindProjectRenderPlanInputV2(application, deterministic),
300
+ );
301
+ }
302
+ throw new ApplicationError(
303
+ "unsupported-plan",
304
+ `Unsupported render.project-plan binding version: ${String(request.operation.version)}`,
305
+ );
306
+ }
307
+ if (
308
+ request.operation.kind === "media.ingest"
309
+ || request.operation.kind === "media.overlay"
310
+ || request.operation.kind === "media.html-overlay"
311
+ || request.operation.kind === "media.audio-effects"
312
+ || request.operation.kind === "media.color-grade"
313
+ ) {
314
+ return JsonValueSchema.parse(
315
+ await bindMediaOperationInput(
316
+ application,
317
+ request.operation.kind,
318
+ deterministic,
319
+ ),
320
+ );
321
+ }
322
+ if (
323
+ request.operation.version === 1
324
+ && (
325
+ request.operation.kind === "slopcamera.diagram.check"
326
+ || request.operation.kind === "slopcamera.diagram.render"
327
+ || request.operation.kind === "slopcamera.image.vectorize"
328
+ )
329
+ ) {
330
+ return JsonValueSchema.parse(
331
+ await bindSlopcameraVisualOperationInput(
332
+ application,
333
+ request.operation.kind,
334
+ deterministic,
335
+ ),
336
+ );
337
+ }
338
+ if (
339
+ request.operation.version === 2
340
+ && (
341
+ request.operation.kind === "slopcamera.diagram.check"
342
+ || request.operation.kind === "slopcamera.diagram.render"
343
+ || request.operation.kind === "slopcamera.image.generate"
344
+ || request.operation.kind === "slopcamera.image.vectorize"
345
+ )
346
+ ) {
347
+ return JsonValueSchema.parse(
348
+ await bindSlopcameraPortableOperationInputV2(
349
+ application,
350
+ request.operation.kind,
351
+ deterministic,
352
+ ),
353
+ );
354
+ }
355
+ if (request.operation.kind === "render.project") {
356
+ if (request.operation.version === 4) return JsonValueSchema.parse(await bindProjectRenderInputV4(application, deterministic));
357
+ if (request.operation.version === 1) {
358
+ return JsonValueSchema.parse(
359
+ await bindProjectRenderInput(application, deterministic),
360
+ );
361
+ }
362
+ if (request.operation.version === 2) {
363
+ return JsonValueSchema.parse(
364
+ await bindProjectRenderInputV2(application, deterministic),
365
+ );
366
+ }
367
+ if (request.operation.version === 3) {
368
+ return JsonValueSchema.parse(
369
+ await bindProjectRenderInputV3(application, deterministic),
370
+ );
371
+ }
372
+ throw new ApplicationError(
373
+ "unsupported-plan",
374
+ `Unsupported render.project binding version: ${String(request.operation.version)}`,
375
+ );
376
+ }
377
+ if (isGatewayOperationKind(request.operation.kind)) {
378
+ const prepared = await prepareGatewayOperation(application, {
379
+ request: gatewayPortRequestForOperation(
380
+ request.operation.kind,
381
+ deterministic,
382
+ ),
383
+ signal: new AbortController().signal,
384
+ });
385
+ return JsonValueSchema.parse(prepared.request);
386
+ }
387
+ return deterministic;
388
+ }
389
+
390
+ function inputDescriptor(request: NodePreparationRequest): JsonValue {
391
+ const input = JsonValueSchema.parse(request.resolvedInput);
392
+ const fingerprint = canonicalJsonFingerprint(
393
+ input,
394
+ "studio.workflow.input-descriptor/v1\0",
395
+ );
396
+ const project = projectReference(input);
397
+ return JsonValueSchema.parse({
398
+ bytes: fingerprint.bytes,
399
+ inputSha256: fingerprint.sha256,
400
+ ...(project === undefined ? {} : { project }),
401
+ });
402
+ }
403
+
404
+ function publishesProject(request: NodeExecutionPlanningRequest): boolean {
405
+ return request.operation.policy.effect === "project-mutation"
406
+ || request.operation.policy.resources.some(
407
+ claim => claim.resource === "project-publication",
408
+ );
409
+ }
410
+
411
+ function publicationKeys(
412
+ request: NodeExecutionPlanningRequest,
413
+ input: JsonValue,
414
+ project: string | undefined,
415
+ ): readonly string[] {
416
+ if (request.operation.kind === "slopcamera.studio.run" && jsonObject(input) && input.job !== undefined && jsonObject(input.job) && typeof input.job.jobId === "string") {
417
+ return [`output:studio:${input.job.jobId}`];
418
+ }
419
+ if (request.operation.version === 2 && jsonObject(input)) {
420
+ if (
421
+ request.operation.kind === "slopcamera.diagram.render"
422
+ || request.operation.kind === "slopcamera.image.generate"
423
+ || request.operation.kind === "slopcamera.image.vectorize"
424
+ ) {
425
+ const publicationParent = slopcameraPortableOutputPublicationParent(
426
+ request.operation.kind,
427
+ input,
428
+ );
429
+ if (publicationParent === undefined) return [];
430
+ return [
431
+ `output:slopcamera:${sha256Hex(
432
+ `studio.workflow.output-publication/v1\0${publicationParent}`,
433
+ )}`,
434
+ ];
435
+ }
436
+ }
437
+ if (
438
+ request.operation.kind === "render.project"
439
+ && jsonObject(input)
440
+ && project !== undefined
441
+ ) {
442
+ const output = input.output;
443
+ if (
444
+ output === undefined
445
+ || !jsonObject(output)
446
+ || typeof output.path !== "string"
447
+ ) return [];
448
+ return [
449
+ `output:${project}:${sha256Hex(
450
+ `studio.workflow.output-publication/v1\0${output.path}`,
451
+ )}`,
452
+ ];
453
+ }
454
+ if (
455
+ request.operation.kind === "render.materialize-selection"
456
+ && jsonObject(input)
457
+ && project !== undefined
458
+ && typeof input.destinationPath === "string"
459
+ ) {
460
+ return [
461
+ `output:${project}:${sha256Hex(
462
+ `studio.workflow.output-publication/v1\0${input.destinationPath}`,
463
+ )}`,
464
+ ];
465
+ }
466
+ if (!publishesProject(request)) return [];
467
+ return project === undefined
468
+ ? [`operation:${request.operation.kind}`]
469
+ : [`project:${project}`];
470
+ }
471
+
472
+ function assertProjectProvenance(
473
+ request: NodeExecutionPlanningRequest,
474
+ project: string | undefined,
475
+ ): void {
476
+ if (project === undefined) return;
477
+ const authorized = request.graphPlan.staticBindings.initialSubjects.some(subject => (
478
+ subject.kind === (request.operation.kind.startsWith("spatial.project.") || (request.operation.kind === "render.project" && request.operation.version === 4) ? "spatial-project" : "project") && subject.id === project
479
+ ));
480
+ if (!authorized) {
481
+ throw new ApplicationError(
482
+ "authorization-required",
483
+ `Node ${request.node.key} received a project identity that was not structurally bound during planning.`,
484
+ {
485
+ operation: request.operation.kind,
486
+ project,
487
+ },
488
+ );
489
+ }
490
+ }
491
+
492
+ function staticProjectGeneration(
493
+ request: NodeExecutionPlanningRequest,
494
+ project: string,
495
+ ): string {
496
+ const subject = request.graphPlan.staticBindings.initialSubjects.find(
497
+ candidate => candidate.kind === "project" && candidate.id === project,
498
+ );
499
+ if (subject === undefined || subject.kind !== "project") {
500
+ throw new ApplicationError(
501
+ "authorization-required",
502
+ `Node ${request.node.key} has no plan-time project generation binding.`,
503
+ { operation: request.operation.kind, project },
504
+ );
505
+ }
506
+ return projectGenerationSha256FromHashes({
507
+ currentPlanSha256: subject.planSha256,
508
+ projectSha256: subject.projectSha256,
509
+ });
510
+ }
511
+
512
+ function nestedGenerationSha256(
513
+ input: JsonValue,
514
+ ): string | undefined {
515
+ if (!jsonObject(input)) return undefined;
516
+ const binding = input.binding;
517
+ if (binding !== undefined && jsonObject(binding)) {
518
+ const projectGenerationSha256 = binding.projectGenerationSha256;
519
+ if (typeof projectGenerationSha256 === "string") {
520
+ return projectGenerationSha256;
521
+ }
522
+ }
523
+ for (const key of ["draft", "revision"] as const) {
524
+ const nested = input[key];
525
+ if (nested === undefined || !jsonObject(nested)) continue;
526
+ const baseGeneration = nested.baseGeneration;
527
+ if (baseGeneration === undefined || !jsonObject(baseGeneration)) continue;
528
+ const generationSha256 = baseGeneration.generationSha256;
529
+ if (typeof generationSha256 === "string") return generationSha256;
530
+ }
531
+ return undefined;
532
+ }
533
+
534
+ function dependencyProjectGeneration(
535
+ request: NodeExecutionPlanningRequest,
536
+ project: string,
537
+ ): string | undefined {
538
+ const candidates = new Set<string>();
539
+ const nodes = new Map(
540
+ request.graphPlan.graph.nodes.map(node => [node.key, node]),
541
+ );
542
+ for (const [nodeKey, output] of Object.entries(request.dependencyOutputs)) {
543
+ const node = nodes.get(nodeKey);
544
+ if (node === undefined || !isOperationGraphNode(node)) continue;
545
+ if (node.executor.operation.kind === "project.snapshot") {
546
+ const parsed = ProjectSnapshotOutputSchema.safeParse(output.value);
547
+ if (
548
+ parsed.success
549
+ && parsed.data.project.projectId === project
550
+ ) {
551
+ candidates.add(parsed.data.generation.generationSha256);
552
+ }
553
+ continue;
554
+ }
555
+ if (node.executor.operation.kind === "project.commit-edits") {
556
+ const parsed = ProjectEditCommitReceiptSchema.safeParse(output.value);
557
+ if (
558
+ parsed.success
559
+ && parsed.data.projectId === project
560
+ ) {
561
+ candidates.add(parsed.data.generation.generationSha256);
562
+ }
563
+ }
564
+ }
565
+ if (candidates.size > 1) {
566
+ throw new ApplicationError(
567
+ "conflict",
568
+ `Node ${request.node.key} depends on incompatible project generations.`,
569
+ { operation: request.operation.kind, project },
570
+ );
571
+ }
572
+ return [...candidates][0];
573
+ }
574
+
575
+ function assertProjectEditBasisProvenance(
576
+ request: NodeExecutionPlanningRequest,
577
+ input: JsonValue,
578
+ project: string,
579
+ ): void {
580
+ if (
581
+ request.operation.kind !== "project.commit-edits"
582
+ && request.operation.kind !== "edit.freeze-revision"
583
+ ) return;
584
+ if (!jsonObject(input)) {
585
+ throw new ApplicationError(
586
+ "authorization-required",
587
+ `Node ${request.node.key} has no typed project edit basis.`,
588
+ );
589
+ }
590
+ const exactBasis = ProjectEditBasisSchema.parse(input.basis);
591
+ const candidates = new Map<string, typeof exactBasis>();
592
+ const nodes = new Map(
593
+ request.graphPlan.graph.nodes.map(node => [node.key, node]),
594
+ );
595
+ for (const [nodeKey, output] of Object.entries(request.dependencyOutputs)) {
596
+ const node = nodes.get(nodeKey);
597
+ if (node === undefined || !isOperationGraphNode(node)) continue;
598
+ if (node.executor.operation.kind === "project.snapshot") {
599
+ const parsed = ProjectSnapshotOutputSchema.safeParse(output.value);
600
+ if (parsed.success && parsed.data.project.projectId === project) {
601
+ candidates.set(canonicalJson(parsed.data.editBasis), parsed.data.editBasis);
602
+ }
603
+ continue;
604
+ }
605
+ if (node.executor.operation.kind === "project.commit-edits") {
606
+ const parsed = ProjectEditCommitReceiptSchema.safeParse(output.value);
607
+ if (parsed.success && parsed.data.projectId === project) {
608
+ candidates.set(canonicalJson(parsed.data.editBasis), parsed.data.editBasis);
609
+ }
610
+ }
611
+ }
612
+ if (
613
+ candidates.size !== 1
614
+ || !candidates.has(canonicalJson(exactBasis))
615
+ ) {
616
+ throw new ApplicationError(
617
+ "authorization-required",
618
+ `Node ${request.node.key} must carry its edit basis directly from one host-owned project snapshot or commit.`,
619
+ { operation: request.operation.kind, project },
620
+ );
621
+ }
622
+ }
623
+
624
+ function assertSpatialProjectBasisProvenance(request: NodeExecutionPlanningRequest, input: JsonValue): void {
625
+ if (!request.operation.kind.startsWith("spatial.project.")
626
+ || request.operation.kind === "spatial.project.reconcile") return;
627
+ if (!jsonObject(input) || typeof input.project !== "string") throw new ApplicationError("authorization-required", "Spatial mutation requires an exact bound project.");
628
+ const expected = SpatialProjectBasisSchema.parse(input.expected);
629
+ const bases = new Set<string>();
630
+ for (const subject of request.graphPlan.staticBindings.initialSubjects) {
631
+ if (subject.kind === "spatial-project" && subject.id === input.project) bases.add(canonicalJson(subject.basis));
632
+ }
633
+ const nodes = new Map(request.graphPlan.graph.nodes.map(node => [node.key, node]));
634
+ for (const [key, output] of Object.entries(request.dependencyOutputs)) {
635
+ const node = nodes.get(key);
636
+ if (node === undefined || !isOperationGraphNode(node)) continue;
637
+ if (node.executor.operation.kind === "spatial.project.snapshot") {
638
+ const snapshot = SpatialProjectSnapshotOutputSchema.safeParse(output.value);
639
+ if (snapshot.success && snapshot.data.projectId === input.project) bases.add(canonicalJson(snapshot.data.basis));
640
+ } else if (node.executor.operation.kind.startsWith("spatial.project.")) {
641
+ const mutation = SpatialProjectMutationOutputSchema.safeParse(output.value);
642
+ if (mutation.success && mutation.data.projectId === input.project && mutation.data.kind === "completed") {
643
+ bases.add(canonicalJson({ version: 2, sha256: mutation.data.projectRevisionSha256 }));
644
+ }
645
+ }
646
+ }
647
+ if (!bases.has(canonicalJson(expected))) throw new ApplicationError("authorization-required", "Spatial mutation basis must come from this project's bound snapshot or a completed typed mutation dependency.");
648
+ }
649
+
650
+ function assertCreativeBaseProvenance(
651
+ request: NodeExecutionPlanningRequest,
652
+ input: JsonValue,
653
+ ): void {
654
+ if (
655
+ request.operation.kind !== "edit.create-candidate-revision"
656
+ && request.operation.kind !== "iteration.create-candidate"
657
+ ) return;
658
+ if (!jsonObject(input)) {
659
+ throw new ApplicationError(
660
+ "authorization-required",
661
+ `Node ${request.node.key} has no exact creative base.`,
662
+ );
663
+ }
664
+ const exactBase = CreativeBaseV1Schema.parse(input.base);
665
+ const candidates = new Set<string>();
666
+ const nodes = new Map(
667
+ request.graphPlan.graph.nodes.map(node => [node.key, node]),
668
+ );
669
+ for (const [nodeKey, output] of Object.entries(request.dependencyOutputs)) {
670
+ const node = nodes.get(nodeKey);
671
+ if (
672
+ node === undefined
673
+ || !isOperationGraphNode(node)
674
+ || node.executor.operation.kind !== "project.snapshot"
675
+ ) continue;
676
+ const snapshot = ProjectSnapshotOutputSchema.safeParse(output.value);
677
+ if (!snapshot.success || snapshot.data.project.projectId !== exactBase.projectId) {
678
+ continue;
679
+ }
680
+ candidates.add(canonicalJson(createCreativeBaseV1({
681
+ currentPlan: snapshot.data.currentPlan,
682
+ project: snapshot.data.project,
683
+ })));
684
+ }
685
+ const authoredInput = request.node.input;
686
+ if (
687
+ typeof authoredInput === "object"
688
+ && authoredInput !== null
689
+ && !Array.isArray(authoredInput)
690
+ && "snapshot" in authoredInput
691
+ ) {
692
+ const persisted = ProjectSnapshotOutputSchema.safeParse(
693
+ authoredInput.snapshot,
694
+ );
695
+ if (persisted.success) {
696
+ candidates.add(canonicalJson(createCreativeBaseV1({
697
+ currentPlan: persisted.data.currentPlan,
698
+ project: persisted.data.project,
699
+ })));
700
+ }
701
+ }
702
+ if (
703
+ candidates.size !== 1
704
+ || !candidates.has(canonicalJson(exactBase))
705
+ ) {
706
+ throw new ApplicationError(
707
+ "authorization-required",
708
+ `Node ${request.node.key} must carry its exact creative base from one host-owned or persisted project snapshot.`,
709
+ {
710
+ operation: request.operation.kind,
711
+ project: exactBase.projectId,
712
+ },
713
+ );
714
+ }
715
+ }
716
+
717
+ function resolveExpectedProjectGeneration(
718
+ request: NodeExecutionPlanningRequest,
719
+ input: JsonValue,
720
+ project: string,
721
+ ): string | undefined {
722
+ // Spatial operations carry their separately versioned aggregate basis.
723
+ if (request.operation.kind.startsWith("spatial.project.") || (request.operation.kind === "render.project" && request.operation.version === 4)) return undefined;
724
+ if (request.operation.kind === "project.snapshot") {
725
+ return staticProjectGeneration(request, project);
726
+ }
727
+ if (
728
+ request.operation.kind === "project.commit-edits"
729
+ || request.operation.kind === "edit.freeze-revision"
730
+ || request.operation.kind === "edit.create-candidate-revision"
731
+ || request.operation.kind === "iteration.create-candidate"
732
+ || request.operation.kind === "iteration.create-matrix"
733
+ || request.operation.kind === "iteration.select"
734
+ || request.operation.kind === "project.promote-selection"
735
+ || request.operation.kind === "render.materialize-selection"
736
+ || request.operation.kind === "render.bind-candidate-output"
737
+ ) {
738
+ return undefined;
739
+ }
740
+ if (
741
+ request.operation.kind === "analysis.faces"
742
+ || request.operation.kind === "analysis.music"
743
+ || request.operation.kind === "analysis.project-inactivity"
744
+ || request.operation.kind === "analysis.project-auto-zooms"
745
+ || request.operation.kind === "analysis.scenes"
746
+ ) {
747
+ // Parallel analyses bind a host-owned ProjectEditBasis in their exact
748
+ // input. That basis tolerates only append-only sibling analysis
749
+ // publication; a full generation hash would make a legitimate sibling
750
+ // completion poison deterministic resume.
751
+ return undefined;
752
+ }
753
+ const explicit = nestedGenerationSha256(input);
754
+ const dependency = dependencyProjectGeneration(request, project);
755
+ if (
756
+ explicit !== undefined
757
+ && dependency !== undefined
758
+ && explicit !== dependency
759
+ ) {
760
+ throw new ApplicationError(
761
+ "conflict",
762
+ `Node ${request.node.key} carries incompatible explicit and dependency project generations.`,
763
+ { operation: request.operation.kind, project },
764
+ );
765
+ }
766
+ const resolved = explicit ?? dependency;
767
+ if (
768
+ resolved === undefined
769
+ && request.operation.policy.preparation.includes("project-state")
770
+ ) {
771
+ throw new ApplicationError(
772
+ "authorization-required",
773
+ `Node ${request.node.key} must carry a typed project snapshot or commit dependency.`,
774
+ { operation: request.operation.kind, project },
775
+ );
776
+ }
777
+ return resolved;
778
+ }
779
+
780
+ export function createApplicationNodePlanner(
781
+ application: ApplicationContext,
782
+ ): SchedulerNodePlanner {
783
+ return {
784
+ prepare: (request): Promise<NodePreparationBinding> => {
785
+ const descriptors = inputDescriptor(request);
786
+ const authoredBytes = jsonObject(descriptors)
787
+ && typeof descriptors.bytes === "number"
788
+ ? descriptors.bytes
789
+ : undefined;
790
+ const upperInputBytes = request.operation.policy.maxInputBytes;
791
+ if (
792
+ authoredBytes === undefined
793
+ || !Number.isSafeInteger(authoredBytes)
794
+ || authoredBytes < 0
795
+ || !Number.isSafeInteger(upperInputBytes)
796
+ || upperInputBytes < 0
797
+ ) {
798
+ throw new ApplicationError(
799
+ "internal",
800
+ `Could not derive an input byte bound for node ${request.node.key}.`,
801
+ );
802
+ }
803
+ if (authoredBytes > upperInputBytes) {
804
+ throw new ApplicationError(
805
+ "invalid-data",
806
+ `Authored input for node ${request.node.key} exceeds the operation's registered input limit.`,
807
+ );
808
+ }
809
+ return Promise.resolve({
810
+ inputDescriptors: descriptors,
811
+ // Host binding may append timestamps, immutable media identities,
812
+ // capability evidence, or provider-normalized fields. Retain the
813
+ // authored byte count in the descriptors while authorizing no more
814
+ // than the operation's registered hard input ceiling.
815
+ upperInputBytes,
816
+ });
817
+ },
818
+ plan: async (request): Promise<NodeExecutionBinding> => {
819
+ // Reject compute-minted paths before any host code opens, hashes, probes,
820
+ // uploads, or prices the referenced file.
821
+ assertOperationFileProvenance(request);
822
+ assertProjectProvenance(
823
+ request,
824
+ projectReference(request.resolvedInput),
825
+ );
826
+ const exactInput = await exactOperationInput(application, request);
827
+ assertSpatialProjectBasisProvenance(request, exactInput);
828
+ assertCreativeBaseProvenance(request, exactInput);
829
+ const project = projectReference(exactInput);
830
+ assertProjectProvenance(request, project);
831
+ if (project !== undefined) {
832
+ assertProjectEditBasisProvenance(request, exactInput, project);
833
+ }
834
+ const keys = publicationKeys(request, exactInput, project);
835
+ let expectedProjectGeneration: string | undefined;
836
+ if (
837
+ project !== undefined
838
+ && request.operation.policy.preparation.includes("project-state")
839
+ ) {
840
+ expectedProjectGeneration = resolveExpectedProjectGeneration(
841
+ request,
842
+ exactInput,
843
+ project,
844
+ );
845
+ }
846
+ return {
847
+ exactInput,
848
+ ...(expectedProjectGeneration === undefined
849
+ ? {}
850
+ : { expectedProjectGeneration }),
851
+ publicationKeys: keys,
852
+ };
853
+ },
854
+ reconcile: async (request): Promise<NodeReconciliation> => (
855
+ await reconcileApplicationNode(request.application, request)
856
+ ),
857
+ reconcileEffect: request => request.operation.kind === "render.project"
858
+ ? reconcileRenderNodeEffect(request.application, request)
859
+ : workflowBoundary("authority", () => reconcileApplicationNode(request.application, request)),
860
+ };
861
+ }
862
+
863
+ function exactReconciliationInput(
864
+ request: NodeReconciliationRequest,
865
+ ): JsonValue {
866
+ return request.executionPlan?.exactInput ?? request.resolvedInput;
867
+ }
868
+
869
+ async function reconcileRenderNode(
870
+ application: ApplicationContext,
871
+ request: NodeReconciliationRequest,
872
+ ): Promise<NodeReconciliation> {
873
+ const executionPlan = request.executionPlan;
874
+ if (executionPlan === undefined) {
875
+ return {
876
+ kind: "incompatible",
877
+ message: "Interrupted render is missing its exact execution plan.",
878
+ };
879
+ }
880
+ const reconcile = request.operation.version === 4 ? reconcileProjectRenderV4 : reconcileProjectRender;
881
+ const result = await reconcile(
882
+ application,
883
+ executionPlan.exactInput,
884
+ {
885
+ nodeKey: request.node.key,
886
+ nodePlanSha256: executionPlan.nodePlanSha256,
887
+ runId: request.runId,
888
+ },
889
+ {
890
+ abortSignal: request.abortSignal,
891
+ beforePublication: request.beforePublication,
892
+ },
893
+ );
894
+ return renderNodeReconciliation(result);
895
+ }
896
+
897
+ function reconcileRenderNodeEffect(
898
+ application: ApplicationContext,
899
+ request: NodeReconciliationRequest,
900
+ ): Effect.Effect<NodeReconciliation, WorkflowFailure> {
901
+ if (request.operation.version === 4) return workflowBoundary("authority", () => reconcileRenderNode(application, request));
902
+ return Effect.gen(function*() {
903
+ const executionPlan = request.executionPlan;
904
+ if (executionPlan === undefined) {
905
+ return { kind: "incompatible" as const, message: "Interrupted render is missing its exact execution plan." };
906
+ }
907
+ const result = yield* reconcileProjectRenderEffect(application, executionPlan.exactInput, {
908
+ nodeKey: request.node.key,
909
+ nodePlanSha256: executionPlan.nodePlanSha256,
910
+ runId: request.runId,
911
+ }, { abortSignal: request.abortSignal, beforePublication: request.beforePublication });
912
+ return yield* workflowValidation("authority", () => renderNodeReconciliation(result));
913
+ });
914
+ }
915
+
916
+ function renderNodeReconciliation(result: ProjectRenderReconciliation | Awaited<ReturnType<typeof reconcileProjectRenderV4>>): NodeReconciliation {
917
+ if (result.kind === "retry") return result;
918
+ if (result.kind === "conflict") {
919
+ return { kind: "incompatible", message: result.message };
920
+ }
921
+ const output = JsonValueSchema.parse(result.output);
922
+ return {
923
+ kind: "completed",
924
+ output,
925
+ receiptReference: result.output.receipt.path,
926
+ summary: {
927
+ bytes: result.output.output.bytes,
928
+ outputPath: result.output.output.path,
929
+ outputSha256: result.output.output.sha256,
930
+ projectId: result.output.output.projectId,
931
+ receiptPath: result.output.receipt.path,
932
+ revisionSha256: result.output.output.revisionSha256,
933
+ },
934
+ };
935
+ }
936
+
937
+ async function reconcileRecordingNode(
938
+ application: ApplicationContext,
939
+ operationKind: RecordingOperationKind,
940
+ ): Promise<NodeReconciliation> {
941
+ const controller = application.recordingController;
942
+ if (controller === undefined) {
943
+ return {
944
+ kind: "ambiguous",
945
+ message: "Recording controller is unavailable for live-state reconciliation.",
946
+ };
947
+ }
948
+ let output;
949
+ try {
950
+ output = parseRecordingOperationOutput(await controller.status());
951
+ } catch (error) {
952
+ return {
953
+ kind: "ambiguous",
954
+ message: error instanceof Error
955
+ ? `Recording state could not be reconciled: ${error.message}`
956
+ : "Recording state could not be reconciled.",
957
+ };
958
+ }
959
+ return {
960
+ kind: "ambiguous",
961
+ message: `Current controller state ${output.state} for ${
962
+ output.recordingId ?? "no active recording"
963
+ } is not an exact receipt proving ${operationKind} completed; issue a new explicit action.`,
964
+ };
965
+ }
966
+
967
+ async function reconcileGatewayNode(
968
+ application: ApplicationContext,
969
+ request: NodeReconciliationRequest,
970
+ operationKind: GatewayOperationKind,
971
+ ): Promise<NodeReconciliation> {
972
+ const executionPlan = request.executionPlan;
973
+ if (executionPlan === undefined) {
974
+ return {
975
+ kind: "incompatible",
976
+ message: "Interrupted Gateway dispatch is missing its exact execution plan.",
977
+ };
978
+ }
979
+ const gatewayRequest = gatewayPortRequestForOperation(
980
+ operationKind,
981
+ executionPlan.exactInput,
982
+ );
983
+ const requestId = gatewayRequestId({
984
+ nodeKey: request.node.key,
985
+ nodePlanSha256: executionPlan.nodePlanSha256,
986
+ operation: gatewayRequest.operation,
987
+ runId: request.runId,
988
+ });
989
+ const reconciliation = await reconcileGatewayOperation(application, {
990
+ request: gatewayRequest,
991
+ requestId,
992
+ signal: request.abortSignal,
993
+ });
994
+ switch (reconciliation.status) {
995
+ case "completed": {
996
+ const summary = summarizeGatewayOperation(
997
+ operationKind,
998
+ reconciliation.result,
999
+ );
1000
+ return {
1001
+ kind: "completed",
1002
+ output: JsonValueSchema.parse(reconciliation.result),
1003
+ receiptReference: reconciliation.result.receipt.path,
1004
+ summary: summary.fields,
1005
+ };
1006
+ }
1007
+ case "not-dispatched":
1008
+ return {
1009
+ kind: "ambiguous",
1010
+ message:
1011
+ "The durable Gateway journal proves no paid call crossed dispatch, but ambiguous paid nodes require a fresh exact approval instead of automatic replay.",
1012
+ };
1013
+ case "dispatched":
1014
+ return {
1015
+ kind: "ambiguous",
1016
+ message:
1017
+ "The durable Gateway journal records paid dispatch without a completed authoritative receipt; Slopcamera will not resubmit it.",
1018
+ };
1019
+ case "failed":
1020
+ return {
1021
+ kind: "incompatible",
1022
+ message:
1023
+ `The Gateway request has an authoritative failure receipt at ${reconciliation.failureReceipt.path}.`,
1024
+ };
1025
+ case "conflict":
1026
+ return {
1027
+ kind: "incompatible",
1028
+ message:
1029
+ "The Gateway journal conflicts with this exact workflow request identity.",
1030
+ };
1031
+ }
1032
+ }
1033
+
1034
+ function missing(error: unknown): boolean {
1035
+ return error instanceof Error && "code" in error && error.code === "ENOENT";
1036
+ }
1037
+
1038
+ async function readProjectTransaction(
1039
+ application: ApplicationContext,
1040
+ project: string,
1041
+ ) {
1042
+ const directory = await resolveProjectDirectory(
1043
+ application.paths.projectRoot,
1044
+ project,
1045
+ );
1046
+ const fileSystem = createNodeBundleFileSystem(directory.path);
1047
+ try {
1048
+ const text = await fileSystem.readText(PROJECT_STATE_TRANSACTION_PATH);
1049
+ return ProjectStateTransactionV1Schema.parse(
1050
+ JSON.parse(text) as unknown,
1051
+ );
1052
+ } catch (error) {
1053
+ if (missing(error)) return null;
1054
+ throw error;
1055
+ }
1056
+ }
1057
+
1058
+ async function reconcileProjectCommit(
1059
+ application: ApplicationContext,
1060
+ request: NodeReconciliationRequest,
1061
+ ): Promise<NodeReconciliation> {
1062
+ const executionPlan = request.executionPlan;
1063
+ const input = exactReconciliationInput(request);
1064
+ const project = projectReference(input);
1065
+ if (
1066
+ executionPlan === undefined
1067
+ || project === undefined
1068
+ || !jsonObject(input)
1069
+ ) {
1070
+ return {
1071
+ kind: "incompatible",
1072
+ message: "Interrupted project commit is missing its exact project-bound plan.",
1073
+ };
1074
+ }
1075
+ return await withProjectPublicationLease(
1076
+ application,
1077
+ "project.commit-edits",
1078
+ { project },
1079
+ async () => {
1080
+ await request.beforePublication();
1081
+ const [transaction, snapshot] = await Promise.all([
1082
+ readProjectTransaction(application, project),
1083
+ openProjectSnapshot(application.paths.projectRoot, project),
1084
+ ]);
1085
+ let parsedInput:
1086
+ | CommitProjectEditsInput
1087
+ | CommitProjectEditsInputV2
1088
+ | CommitProjectEditsInputV3;
1089
+ if (request.operation.version === 1) {
1090
+ parsedInput = CommitProjectEditsInputSchema.parse(input);
1091
+ } else if (request.operation.version === 2) {
1092
+ parsedInput = CommitProjectEditsInputV2Schema.parse(input);
1093
+ } else if (request.operation.version === 3) {
1094
+ parsedInput = CommitProjectEditsInputV3Schema.parse(input);
1095
+ } else {
1096
+ return {
1097
+ kind: "incompatible",
1098
+ message:
1099
+ `Interrupted project commit uses unsupported operation version ${request.operation.version}.`,
1100
+ };
1101
+ }
1102
+ if (projectMatchesEditBasis(parsedInput.basis, snapshot)) {
1103
+ return { kind: "retry" };
1104
+ }
1105
+ const transactionId = projectEditTransactionId(
1106
+ executionPlan.nodePlanSha256,
1107
+ );
1108
+ if (
1109
+ transaction?.transactionId !== transactionId
1110
+ || transaction.phase !== "settled"
1111
+ || transaction.active !== "after"
1112
+ ) {
1113
+ return {
1114
+ kind: "incompatible",
1115
+ message: "Project state changed without this node's settled transaction receipt.",
1116
+ };
1117
+ }
1118
+ const directory = await resolveProjectDirectory(
1119
+ application.paths.projectRoot,
1120
+ project,
1121
+ );
1122
+ const fileSystem = createNodeBundleFileSystem(directory.path);
1123
+ const [transactionProject, transactionPlan] = await Promise.all([
1124
+ loadVideoProject(fileSystem, transaction.after.project.path),
1125
+ loadProjectEditPlan(fileSystem, transaction.after.plan.path),
1126
+ ]);
1127
+ const output = projectEditCommitReceipt(
1128
+ parsedInput,
1129
+ transactionProject,
1130
+ transactionPlan,
1131
+ );
1132
+ if (
1133
+ transaction.after.plan.sha256 !== output.planHash
1134
+ || transaction.after.project.sha256
1135
+ !== output.generation.projectSha256
1136
+ || transaction.after.project.sha256
1137
+ !== canonicalJsonSha256(transactionProject)
1138
+ || transaction.after.plan.sha256
1139
+ !== canonicalJsonSha256(transactionPlan)
1140
+ || !projectMatchesEditBasis(output.editBasis, snapshot)
1141
+ ) {
1142
+ return {
1143
+ kind: "incompatible",
1144
+ message: "Settled project transaction does not match the current generation.",
1145
+ };
1146
+ }
1147
+ return {
1148
+ kind: "completed",
1149
+ output: JsonValueSchema.parse(output),
1150
+ summary: {
1151
+ generationSha256: output.generation.generationSha256,
1152
+ operation: output.operation,
1153
+ planHash: output.planHash,
1154
+ planId: output.planId,
1155
+ projectId: output.projectId,
1156
+ },
1157
+ };
1158
+ },
1159
+ request.beforePublication,
1160
+ );
1161
+ }
1162
+
1163
+ async function reconcileVariantPromotionNode(
1164
+ application: ApplicationContext,
1165
+ request: NodeReconciliationRequest,
1166
+ ): Promise<NodeReconciliation> {
1167
+ const executionPlan = request.executionPlan;
1168
+ if (executionPlan === undefined) {
1169
+ return {
1170
+ kind: "incompatible",
1171
+ message: "Interrupted editorial promotion is missing its exact project-bound plan.",
1172
+ };
1173
+ }
1174
+ const project = projectReference(executionPlan.exactInput);
1175
+ if (project === undefined) {
1176
+ return {
1177
+ kind: "incompatible",
1178
+ message: "Interrupted editorial promotion has no exact project identity.",
1179
+ };
1180
+ }
1181
+ return await withProjectPublicationLease(
1182
+ application,
1183
+ "project.promote-selection",
1184
+ { project },
1185
+ async () => {
1186
+ await request.beforePublication();
1187
+ const reconciliation = await reconcileVariantSelectionPromotion(
1188
+ application,
1189
+ executionPlan.exactInput,
1190
+ );
1191
+ if (reconciliation.kind === "retry") return reconciliation;
1192
+ if (reconciliation.kind === "conflict") {
1193
+ return { kind: "incompatible", message: reconciliation.message };
1194
+ }
1195
+ const output = reconciliation.output;
1196
+ return {
1197
+ kind: "completed",
1198
+ output: JsonValueSchema.parse(output),
1199
+ receiptReference: output.artifact.path,
1200
+ summary: {
1201
+ projectId: output.projectId,
1202
+ promotedPlanSha256: output.promotedPlanSha256,
1203
+ promotionSha256: output.promotionSha256,
1204
+ selectionSha256: output.selectionSha256,
1205
+ },
1206
+ };
1207
+ },
1208
+ request.beforePublication,
1209
+ );
1210
+ }
1211
+
1212
+ async function reconcileApplicationNode(
1213
+ application: ApplicationContext,
1214
+ request: NodeReconciliationRequest,
1215
+ ): Promise<NodeReconciliation> {
1216
+ if (request.operation.kind === "render.project") {
1217
+ return await reconcileRenderNode(application, request);
1218
+ }
1219
+ if (isGatewayOperationKind(request.operation.kind)) {
1220
+ return await reconcileGatewayNode(
1221
+ application,
1222
+ request,
1223
+ request.operation.kind,
1224
+ );
1225
+ }
1226
+ if (isRecordingOperationKind(request.operation.kind)) {
1227
+ return await reconcileRecordingNode(
1228
+ application,
1229
+ request.operation.kind,
1230
+ );
1231
+ }
1232
+ if (request.operation.kind === "project.commit-edits") {
1233
+ return await reconcileProjectCommit(application, request);
1234
+ }
1235
+ if (request.operation.kind === "project.promote-selection") {
1236
+ return await reconcileVariantPromotionNode(application, request);
1237
+ }
1238
+ if (hasLocalVerifiedReceiptReconciler(request.operation.kind)) {
1239
+ const executionPlan = request.executionPlan;
1240
+ const workspaceDirectory = request.workspaceDirectory;
1241
+ if (
1242
+ executionPlan === undefined
1243
+ || workspaceDirectory === undefined
1244
+ ) {
1245
+ return {
1246
+ kind: "incompatible",
1247
+ message:
1248
+ "Interrupted verified-receipt operation is missing its exact private recovery workspace.",
1249
+ };
1250
+ }
1251
+ const reconciliation = await reconcileLocalVerifiedReceiptOperation(
1252
+ application,
1253
+ {
1254
+ abortSignal: request.abortSignal,
1255
+ beforePublication: request.beforePublication,
1256
+ exactInput: executionPlan.exactInput,
1257
+ ...(executionPlan.expectedProjectGeneration === undefined
1258
+ ? {}
1259
+ : {
1260
+ expectedProjectGeneration:
1261
+ executionPlan.expectedProjectGeneration,
1262
+ }),
1263
+ identity: {
1264
+ inputSchemaId: request.operation.inputSchemaId,
1265
+ kind: request.operation.kind,
1266
+ nodeKey: request.node.key,
1267
+ nodePlanSha256: executionPlan.nodePlanSha256,
1268
+ outputSchemaId: request.operation.outputSchemaId,
1269
+ runId: request.runId,
1270
+ version: request.operation.version,
1271
+ },
1272
+ workspaceDirectory,
1273
+ },
1274
+ );
1275
+ if (reconciliation.kind !== "completed") return reconciliation;
1276
+ return {
1277
+ ...reconciliation,
1278
+ output: JsonValueSchema.parse(reconciliation.output),
1279
+ };
1280
+ }
1281
+ if (request.resumeClass === "deterministic") {
1282
+ return { kind: "retry" };
1283
+ }
1284
+ return {
1285
+ kind: "ambiguous",
1286
+ message: `Operation ${request.operation.kind} has no application reconciliation boundary.`,
1287
+ };
1288
+ }